diff --git a/.changeset/minor-enable-reaction-status-comment-by-default.md b/.changeset/minor-enable-reaction-status-comment-by-default.md
new file mode 100644
index 00000000000..486279cf7f5
--- /dev/null
+++ b/.changeset/minor-enable-reaction-status-comment-by-default.md
@@ -0,0 +1,7 @@
+---
+"gh-aw": minor
+---
+
+Enable `reaction: eyes` and `status-comment: true` by default when `slash_command` or `label_command` triggers are used. Both can be disabled explicitly with `reaction: none` and `status-comment: false`.
+
+Previously, `reaction: eyes` was only auto-enabled for `slash_command` workflows, and `status-comment` always required explicit opt-in. Now both defaults apply to `label_command` workflows as well, and `status-comment: true` is the default for both command trigger types.
diff --git a/.changeset/patch-add-label-command-trigger.md b/.changeset/patch-add-label-command-trigger.md
new file mode 100644
index 00000000000..1d59bc38690
--- /dev/null
+++ b/.changeset/patch-add-label-command-trigger.md
@@ -0,0 +1,5 @@
+---
+"gh-aw": patch
+---
+
+Add support for the `label_command` trigger so workflows can run when a configured label is added to an issue, pull request, or discussion. The activation job now removes the triggering label at startup and exposes `needs.activation.outputs.label_command` for downstream use.
diff --git a/.changeset/patch-bump-awf-v0-24-2.md b/.changeset/patch-bump-awf-v0-24-2.md
new file mode 100644
index 00000000000..c061c311bde
--- /dev/null
+++ b/.changeset/patch-bump-awf-v0-24-2.md
@@ -0,0 +1,5 @@
+---
+"gh-aw": patch
+---
+
+Bump the default gh-aw-firewall version to v0.24.2.
diff --git a/.changeset/patch-gh-aw-home-path-constants.md b/.changeset/patch-gh-aw-home-path-constants.md
new file mode 100644
index 00000000000..ca3b0f9d957
--- /dev/null
+++ b/.changeset/patch-gh-aw-home-path-constants.md
@@ -0,0 +1,5 @@
+---
+"gh-aw": patch
+---
+
+Refactor workflow compilation to replace hardcoded `/opt/gh-aw` paths with `GH_AW_HOME`-based constants, enabling self-hosted runners to relocate the installation directory via the `GH_AW_HOME` environment variable.
diff --git a/.changeset/patch-runtime-safe-outputs-tools-generation.md b/.changeset/patch-runtime-safe-outputs-tools-generation.md
new file mode 100644
index 00000000000..c211d0bbf7f
--- /dev/null
+++ b/.changeset/patch-runtime-safe-outputs-tools-generation.md
@@ -0,0 +1,5 @@
+---
+"gh-aw": patch
+---
+
+Load safe outputs tool definitions at runtime from `actions/setup` via `tools_meta.json` instead of inlining filtered JSON into each compiled lock file, reducing lock-file size and avoiding full workflow recompiles when tool descriptions change.
diff --git a/.changeset/patch-update-go-sdk-security-fix.md b/.changeset/patch-update-go-sdk-security-fix.md
new file mode 100644
index 00000000000..50fdb0d6498
--- /dev/null
+++ b/.changeset/patch-update-go-sdk-security-fix.md
@@ -0,0 +1,5 @@
+---
+"gh-aw": patch
+---
+
+Update `github.com/modelcontextprotocol/go-sdk` to v1.4.1 to apply the upstream security patch for JSON parsing and add HTTP streamable MCP request hardening.
diff --git a/.github/aw/github-agentic-workflows.md b/.github/aw/github-agentic-workflows.md
index d41ffaba885..d6e53000b6c 100644
--- a/.github/aw/github-agentic-workflows.md
+++ b/.github/aw/github-agentic-workflows.md
@@ -531,6 +531,7 @@ The YAML frontmatter supports these fields:
discussions: true # Optional: set false to exclude discussions:write permission (default: true)
issues: true # Optional: set false to exclude issues:write permission (default: true)
pull-requests: true # Optional: set false to exclude pull-requests:write permission (default: true)
+ footer: true # Optional: when false, omits visible footer but preserves XML markers (default: true)
target-repo: "owner/repo" # Optional: cross-repository
```
@@ -1136,6 +1137,9 @@ The YAML frontmatter supports these fields:
- `report-failure-as-issue:` - Control whether workflow failures are reported as GitHub issues (boolean, default: `true`)
- When `false`, suppresses automatic failure issue creation for this workflow
- Use to silence noisy failure reports for workflows where failures are expected or handled externally
+ - `failure-issue-repo:` - Repository to create failure tracking issues in (string, format: `"owner/repo"`)
+ - Defaults to the current repository when not specified
+ - Use when the current repository has issues disabled: `failure-issue-repo: "myorg/infra-alerts"`
- `id-token:` - Override the id-token permission for the safe-outputs job (string: `"write"` or `"none"`)
- `"write"`: force-enable `id-token: write` permission (required for OIDC authentication with cloud providers)
- `"none"`: suppress automatic detection and prevent adding `id-token: write` even when vault/OIDC actions are detected in steps
diff --git a/.github/workflows/ace-editor.lock.yml b/.github/workflows/ace-editor.lock.yml
index 6526c68b595..031660adfbc 100644
--- a/.github/workflows/ace-editor.lock.yml
+++ b/.github/workflows/ace-editor.lock.yml
@@ -53,8 +53,9 @@ jobs:
pull-requests: write
outputs:
body: ${{ steps.sanitized.outputs.body }}
- comment_id: ""
- comment_repo: ""
+ comment_id: ${{ steps.add-comment.outputs.comment-id }}
+ comment_repo: ${{ steps.add-comment.outputs.comment-repo }}
+ comment_url: ${{ steps.add-comment.outputs.comment-url }}
model: ${{ steps.generate_aw_info.outputs.model }}
secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
slash_command: ${{ needs.pre_activation.outputs.matched_command }}
@@ -71,7 +72,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -86,18 +87,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "false"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -118,9 +119,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -128,18 +129,30 @@ jobs:
GH_AW_WORKFLOW_FILE: "ace-editor.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_WORKFLOW_NAME: "ACE Editor Session"
+ with:
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_workflow_run_comment.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -153,14 +166,14 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
cat << 'GH_AW_PROMPT_EOF'
The following GitHub context information is available for this workflow:
@@ -191,6 +204,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -204,9 +218,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -224,10 +238,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_MATCHED_COMMAND: ${{ needs.pre_activation.outputs.matched_command }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -248,11 +262,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -273,6 +287,7 @@ jobs:
issues: read
pull-requests: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: aceeditor
outputs:
inference_access_error: ${{ steps.detect-inference-error.outputs.inference_access_error || 'false' }}
@@ -288,13 +303,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -308,11 +327,11 @@ jobs:
git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
echo "Git configured with standard GitHub Actions identity"
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -321,14 +340,15 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0
- name: Start MCP Gateway
id: start-mcp-gateway
env:
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -346,10 +366,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -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 -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -357,10 +377,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
}
},
@@ -378,7 +403,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -387,7 +413,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -414,7 +440,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -452,15 +478,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -470,7 +496,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Parse agent logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -478,18 +504,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -570,7 +596,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for command workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -579,9 +605,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check command position
id: check_command_position
@@ -590,8 +616,8 @@ jobs:
GH_AW_COMMANDS: "[\"ace\"]"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_command_position.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_command_position.cjs');
await main();
diff --git a/.github/workflows/agent-performance-analyzer.lock.yml b/.github/workflows/agent-performance-analyzer.lock.yml
index 2a819181f40..9e6e5bf8244 100644
--- a/.github/workflows/agent-performance-analyzer.lock.yml
+++ b/.github/workflows/agent-performance-analyzer.lock.yml
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,14 +80,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -104,9 +104,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "agent-performance-analyzer.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -122,16 +122,17 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, create_issue, create_discussion, missing_tool, missing_data, noop
@@ -165,6 +166,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -181,9 +183,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -206,10 +208,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -235,11 +237,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -267,10 +269,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: agentperformanceanalyzer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -292,7 +295,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -328,7 +331,11 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Repo memory git-based storage configuration from frontmatter processed below
- name: Clone repo-memory branch (default)
env:
@@ -338,7 +345,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -361,16 +368,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -379,10 +386,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -397,259 +404,38 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":10},"create_discussion":{"expires":24,"max":2},"create_issue":{"expires":48,"group":true,"max":5},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 5 issue(s) can be created. Labels [\"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 2 discussion(s) can be created.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 10 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 10 comment(s) can be added.",
+ "create_discussion": " CONSTRAINTS: Maximum 2 discussion(s) can be created.",
+ "create_issue": " CONSTRAINTS: Maximum 5 issue(s) can be created. Labels [\"cookie\"] will be automatically added."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -787,6 +573,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -811,8 +598,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -823,7 +610,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -831,7 +618,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -850,10 +638,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -866,6 +654,13 @@ jobs:
"GITHUB_TOKEN": "\${GITHUB_TOKEN}",
"GITHUB_ACTOR": "\${GITHUB_ACTOR}",
"GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
@@ -873,10 +668,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,actions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -884,6 +684,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -901,7 +708,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -910,7 +718,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -939,7 +747,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -977,15 +785,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -994,7 +802,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1011,9 +819,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1022,18 +830,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1117,9 +925,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1142,7 +950,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1170,9 +978,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1218,6 +1026,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-agent-performance-analyzer"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1233,7 +1043,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1257,9 +1067,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1270,9 +1080,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1297,9 +1107,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1314,9 +1124,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
pre_activation:
@@ -1337,7 +1147,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1346,9 +1156,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
push_repo_memory:
@@ -1360,6 +1170,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1375,7 +1187,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1418,9 +1230,9 @@ jobs:
FILE_GLOB_FILTER: "**"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1436,6 +1248,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/agent-performance-analyzer"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "agent-performance-analyzer"
GH_AW_WORKFLOW_NAME: "Agent Performance Analyzer - Meta-Orchestrator"
outputs:
@@ -1460,7 +1273,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1486,9 +1299,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/agent-persona-explorer.lock.yml b/.github/workflows/agent-persona-explorer.lock.yml
index ccee4995d51..df95c40b6db 100644
--- a/.github/workflows/agent-persona-explorer.lock.yml
+++ b/.github/workflows/agent-persona-explorer.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,18 +81,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -110,9 +110,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "agent-persona-explorer.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -127,16 +127,17 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -170,6 +171,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -186,9 +188,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -208,10 +210,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -234,11 +236,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -265,10 +267,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: agentpersonaexplorer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -290,7 +293,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -326,10 +329,14 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -359,16 +366,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -377,10 +384,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -395,158 +402,36 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"agent-research\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"agent-research\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -633,6 +518,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -657,8 +543,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -669,7 +555,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -677,7 +563,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -696,10 +583,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -712,6 +599,13 @@ jobs:
"GITHUB_TOKEN": "\${GITHUB_TOKEN}",
"GITHUB_ACTOR": "\${GITHUB_ACTOR}",
"GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
@@ -719,10 +613,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -730,6 +629,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -747,7 +653,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -756,7 +663,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -784,7 +691,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -822,15 +729,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -840,7 +747,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -857,9 +764,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -868,18 +775,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -960,9 +867,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -985,7 +892,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1012,9 +919,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1059,6 +966,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-agent-persona-explorer"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1074,7 +983,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1098,9 +1007,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1111,9 +1020,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1135,9 +1044,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1152,9 +1061,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
pre_activation:
@@ -1175,7 +1084,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1184,9 +1093,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
safe_outputs:
@@ -1201,6 +1110,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/agent-persona-explorer"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "agent-persona-explorer"
GH_AW_WORKFLOW_NAME: "Agent Persona Explorer"
outputs:
@@ -1221,7 +1131,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1247,9 +1157,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1266,6 +1176,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: agentpersonaexplorer
steps:
- name: Checkout actions folder
@@ -1278,7 +1189,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/agentics-maintenance.yml b/.github/workflows/agentics-maintenance.yml
index 211c2d107fe..853959aed25 100644
--- a/.github/workflows/agentics-maintenance.yml
+++ b/.github/workflows/agentics-maintenance.yml
@@ -71,33 +71,33 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Close expired discussions
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/close_expired_discussions.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/close_expired_discussions.cjs');
await main();
- name: Close expired issues
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/close_expired_issues.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/close_expired_issues.cjs');
await main();
- name: Close expired pull requests
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/close_expired_pull_requests.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/close_expired_pull_requests.cjs');
await main();
run_operation:
@@ -116,16 +116,16 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check admin/maintainer permissions
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_team_member.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_team_member.cjs');
await main();
- name: Setup Go
@@ -146,9 +146,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/run_operation_update_upgrade.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/run_operation_update_upgrade.cjs');
await main();
compile-workflows:
@@ -180,15 +180,15 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check for out-of-sync workflows and create issue if needed
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_recompile_needed.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_recompile_needed.cjs');
await main();
zizmor-scan:
@@ -236,7 +236,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Validate Secrets
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -254,9 +254,9 @@ jobs:
NOTION_API_TOKEN: ${{ secrets.NOTION_API_TOKEN }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/validate_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/validate_secrets.cjs');
await main();
- name: Upload secret validation report
diff --git a/.github/workflows/ai-moderator.lock.yml b/.github/workflows/ai-moderator.lock.yml
index b371944f782..5917dc767cf 100644
--- a/.github/workflows/ai-moderator.lock.yml
+++ b/.github/workflows/ai-moderator.lock.yml
@@ -84,7 +84,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -99,18 +99,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate CODEX_API_KEY or OPENAI_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
@@ -129,18 +129,18 @@ jobs:
GH_AW_WORKFLOW_FILE: "ai-moderator.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/compute_text.cjs');
await main();
- name: Lock issue for agent workflow
id: lock-issue
@@ -148,9 +148,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/lock-issue.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/lock-issue.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -166,16 +166,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_labels, hide_comment, missing_tool, missing_data, noop
@@ -209,6 +209,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -224,9 +225,9 @@ jobs:
GH_AW_EXPR_799BE623: ${{ github.event.issue.number || github.event.pull_request.number }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -247,10 +248,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -274,11 +275,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -301,10 +302,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: aimoderator
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -323,12 +325,16 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Cache cache-memory file share data
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -336,18 +342,6 @@ jobs:
path: /tmp/gh-aw/cache-memory
restore-keys: |
spam-tracking-${{ github.repository_owner }}-
- - name: Configure Git credentials
- env:
- REPO_NAME: ${{ github.repository }}
- SERVER_URL: ${{ github.server_url }}
- run: |
- git config --global user.email "github-actions[bot]@users.noreply.github.com"
- git config --global user.name "github-actions[bot]"
- git config --global am.keepcr true
- # Re-authenticate git with GitHub token
- SERVER_URL_STRIPPED="${SERVER_URL#https://}"
- git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
- echo "Git configured with standard GitHub Actions identity"
- name: Checkout PR branch
id: checkout-pr
if: |
@@ -358,9 +352,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -370,7 +364,7 @@ jobs:
- name: Install Codex
run: npm install -g @openai/codex@latest
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -379,183 +373,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_labels":{"allowed":["spam","ai-generated","link-spam","ai-inspected"],"max":3,"target":"*"},"hide_comment":{"allowed_reasons":["spam"],"max":5},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Add labels to an existing GitHub issue or pull request for categorization and filtering. Labels must already exist in the repository. For creating new issues with labels, use create_issue with the labels property instead. CONSTRAINTS: Only these labels are allowed: [\"spam\" \"ai-generated\" \"link-spam\" \"ai-inspected\"]. Target: *.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). If omitted, adds labels to the issue or PR that triggered this workflow. Only works for issue or pull_request event triggers. For schedule, workflow_dispatch, or other triggers, item_number is required — omitting it will silently skip the label operation.",
- "type": "number"
- },
- "labels": {
- "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "add_labels"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Hide a comment on a GitHub issue, pull request, or discussion. This collapses the comment and marks it as spam, abuse, off-topic, outdated, or resolved. Use this for inappropriate, off-topic, or outdated comments. The comment_id must be a GraphQL node ID (string like 'IC_kwDOABCD123456'), not a numeric REST API comment ID. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.hide-comment configuration to exclude this permission.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "comment_id": {
- "description": "GraphQL node ID of the comment to hide (e.g., 'IC_kwDOABCD123456'). This is the GraphQL node ID, not the numeric comment ID from REST API. Can be obtained from GraphQL queries or comment API responses.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Optional reason for hiding the comment. Defaults to SPAM if not provided. Valid values: SPAM (spam content), ABUSE (abusive/harassment content), OFF_TOPIC (not relevant to discussion), OUTDATED (no longer applicable), RESOLVED (issue/question has been resolved).",
- "enum": [
- "SPAM",
- "ABUSE",
- "OFF_TOPIC",
- "OUTDATED",
- "RESOLVED"
- ],
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "comment_id"
- ],
- "type": "object"
- },
- "name": "hide_comment"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_labels": " CONSTRAINTS: Only these labels are allowed: [\"spam\" \"ai-generated\" \"link-spam\" \"ai-inspected\"]. Target: *."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_labels": {
"defaultMax": 5,
@@ -659,6 +500,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -683,8 +525,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -695,7 +537,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -703,7 +545,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -721,7 +564,7 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="codex"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
cat > /tmp/gh-aw/mcp-config/config.toml << GH_AW_MCP_CONFIG_EOF
[history]
@@ -745,20 +588,30 @@ jobs:
[mcp_servers.safeoutputs.headers]
Authorization = "$GH_AW_SAFE_OUTPUTS_API_KEY"
+
+ [mcp_servers.safeoutputs."guard-policies"]
+
+ [mcp_servers.safeoutputs."guard-policies".write-sink]
+ accept = ["*"]
GH_AW_MCP_CONFIG_EOF
# Generate JSON config for MCP gateway
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -766,6 +619,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -783,13 +643,14 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Codex
run: |
set -o pipefail
mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" && codex ${GH_AW_MODEL_AGENT_CODEX:+-c model="$GH_AW_MODEL_AGENT_CODEX" }exec -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
@@ -808,18 +669,6 @@ jobs:
GIT_COMMITTER_NAME: github-actions[bot]
OPENAI_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
RUST_LOG: trace,hyper_util=info,mio=info,reqwest=info,os_info=info,codex_otel=warn,codex_core=debug,ocodex_exec=debug
- - name: Configure Git credentials
- env:
- REPO_NAME: ${{ github.repository }}
- SERVER_URL: ${{ github.server_url }}
- run: |
- git config --global user.email "github-actions[bot]@users.noreply.github.com"
- git config --global user.name "github-actions[bot]"
- git config --global am.keepcr true
- # Re-authenticate git with GitHub token
- SERVER_URL_STRIPPED="${SERVER_URL#https://}"
- git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
- echo "Git configured with standard GitHub Actions identity"
- name: Stop MCP Gateway
if: always()
continue-on-error: true
@@ -828,15 +677,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'CODEX_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY'
@@ -847,7 +696,7 @@ jobs:
SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -864,9 +713,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -875,18 +724,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_codex_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_codex_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -908,9 +757,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { validateMemoryFiles } = require('/opt/gh-aw/actions/validate_memory_files.cjs');
+ const { validateMemoryFiles } = require(process.env.GH_AW_HOME + '/actions/validate_memory_files.cjs');
const allowedExtensions = [".json"];
const result = validateMemoryFiles('/tmp/gh-aw/cache-memory', 'cache', allowedExtensions);
if (!result.valid) {
@@ -950,6 +799,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-ai-moderator"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -965,7 +816,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -989,9 +840,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1002,9 +853,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1023,9 +874,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1040,9 +891,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
pre_activation:
@@ -1064,7 +915,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check user rate limit
id: check_rate_limit
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1076,9 +927,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_rate_limit.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_rate_limit.cjs');
await main();
- name: Check skip-roles
id: check_skip_roles
@@ -1089,9 +940,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_skip_roles.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_skip_roles.cjs');
await main();
- name: Check skip-bots
id: check_skip_bots
@@ -1101,9 +952,9 @@ jobs:
GH_AW_WORKFLOW_NAME: "AI Moderator"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_skip_bots.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_skip_bots.cjs');
await main();
safe_outputs:
@@ -1122,6 +973,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/ai-moderator"
GH_AW_ENGINE_ID: "codex"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "ai-moderator"
GH_AW_WORKFLOW_NAME: "AI Moderator"
outputs:
@@ -1142,7 +994,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1168,9 +1020,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1201,15 +1053,15 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Unlock issue after agent workflow
id: unlock-issue
if: ((github.event_name == 'issues') || (github.event_name == 'issue_comment')) && (needs.activation.outputs.issue_locked == 'true')
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/unlock-issue.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/unlock-issue.cjs');
await main();
diff --git a/.github/workflows/archie.lock.yml b/.github/workflows/archie.lock.yml
index 96aebc9a0a7..35fcbd8d6f3 100644
--- a/.github/workflows/archie.lock.yml
+++ b/.github/workflows/archie.lock.yml
@@ -89,7 +89,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -104,14 +104,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -131,9 +131,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -141,18 +141,18 @@ jobs:
GH_AW_WORKFLOW_FILE: "archie.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/compute_text.cjs');
await main();
- name: Add comment with workflow run link
id: add-comment
@@ -163,9 +163,9 @@ jobs:
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 📊 *Diagram rendered by [{workflow_name}]({run_url})*{history_link}\",\"footerWorkflowRecompile\":\"\\u003e 🔧 *Workflow sync report by [{workflow_name}]({run_url}) for {repository}*\",\"footerWorkflowRecompileComment\":\"\\u003e 🔄 *Update from [{workflow_name}]({run_url}) for {repository}*\",\"runStarted\":\"📐 [{workflow_name}]({run_url}) is analyzing the architecture for this {event_type}...\",\"runSuccess\":\"🎨 [{workflow_name}]({run_url}) has completed the architecture visualization. ✅\",\"runFailure\":\"📐 [{workflow_name}]({run_url}) encountered an issue and could not complete the architecture diagram. Check the [run logs]({run_url}) for details.\"}"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_workflow_run_comment.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_workflow_run_comment.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -183,15 +183,15 @@ jobs:
GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }}
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, missing_tool, missing_data, noop
@@ -225,8 +225,9 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
- cat "/opt/gh-aw/prompts/pr_context_prompt.md"
+ cat "${GH_AW_HOME}/prompts/pr_context_prompt.md"
fi
cat << 'GH_AW_PROMPT_EOF'
@@ -249,9 +250,9 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -272,10 +273,10 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -299,11 +300,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -328,10 +329,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: archie
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -353,13 +355,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -382,16 +388,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -400,155 +406,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -627,6 +508,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -651,8 +533,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -663,7 +545,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -671,7 +553,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -689,10 +572,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -700,10 +583,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -711,6 +599,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -719,7 +614,14 @@ jobs:
"args": ["--network", "host"],
"entrypoint": "serena",
"entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -736,7 +638,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -745,7 +648,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -774,7 +677,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -812,15 +715,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -829,7 +732,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -847,9 +750,9 @@ jobs:
GH_AW_COMMAND: archie
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -858,18 +761,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -944,9 +847,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -969,7 +872,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -997,9 +900,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1044,6 +947,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-archie"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1059,7 +964,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1083,9 +988,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1096,9 +1001,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1118,9 +1023,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1135,9 +1040,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Update reaction comment with completion status
id: conclusion
@@ -1154,9 +1059,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/notify_comment_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/notify_comment_error.cjs');
await main();
pre_activation:
@@ -1183,7 +1088,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for command workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1192,9 +1097,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check command position
id: check_command_position
@@ -1203,9 +1108,9 @@ jobs:
GH_AW_COMMANDS: "[\"archie\"]"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_command_position.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_command_position.cjs');
await main();
safe_outputs:
@@ -1221,6 +1126,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/archie"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 📊 *Diagram rendered by [{workflow_name}]({run_url})*{history_link}\",\"footerWorkflowRecompile\":\"\\u003e 🔧 *Workflow sync report by [{workflow_name}]({run_url}) for {repository}*\",\"footerWorkflowRecompileComment\":\"\\u003e 🔄 *Update from [{workflow_name}]({run_url}) for {repository}*\",\"runStarted\":\"📐 [{workflow_name}]({run_url}) is analyzing the architecture for this {event_type}...\",\"runSuccess\":\"🎨 [{workflow_name}]({run_url}) has completed the architecture visualization. ✅\",\"runFailure\":\"📐 [{workflow_name}]({run_url}) encountered an issue and could not complete the architecture diagram. Check the [run logs]({run_url}) for details.\"}"
GH_AW_WORKFLOW_ID: "archie"
GH_AW_WORKFLOW_NAME: "Archie"
@@ -1244,7 +1150,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1270,9 +1176,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml
index 04cd2f57ca3..024e1ad2a14 100644
--- a/.github/workflows/artifacts-summary.lock.yml
+++ b/.github/workflows/artifacts-summary.lock.yml
@@ -28,7 +28,7 @@
# - shared/reporting.md
# - shared/safe-output-app.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"ec4bf7cb905b85ccdcc95c21b2ed16912b1c6737fcbfcecb1cbc252559791663","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"e3834de983907b6c87bda48a7f7b2531de75e4fd6b5af04b068c91cc68d4dfe2","strict":true}
name: "Artifacts Summary"
"on":
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,14 +79,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","node"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -103,9 +103,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "artifacts-summary.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -120,15 +120,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -162,6 +162,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -182,9 +183,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -200,10 +201,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -222,11 +223,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -251,10 +252,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: artifactssummary
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -276,13 +278,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -305,16 +311,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -323,152 +329,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"artifacts\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"artifacts\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -555,6 +439,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -579,8 +464,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -591,7 +476,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -599,7 +484,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -617,10 +503,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -628,10 +514,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "actions,repos"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -639,6 +530,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -656,7 +554,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -665,7 +564,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -694,7 +593,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -732,15 +631,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -749,7 +648,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -766,9 +665,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -777,18 +676,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -863,9 +762,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -888,7 +787,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -916,9 +815,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -962,6 +861,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-artifacts-summary"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -977,7 +878,19 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
+ - name: Generate GitHub App token
+ id: safe-outputs-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
+ permission-contents: read
+ permission-discussions: write
+ permission-issues: write
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -999,11 +912,11 @@ jobs:
GH_AW_NOOP_MAX: "1"
GH_AW_WORKFLOW_NAME: "Artifacts Summary"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1012,11 +925,11 @@ jobs:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
GH_AW_WORKFLOW_NAME: "Artifacts Summary"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1035,11 +948,11 @@ jobs:
GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
GH_AW_TIMEOUT_MINUTES: "15"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1052,12 +965,25 @@ jobs:
GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }}
GH_AW_NOOP_REPORT_AS_ISSUE: "true"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
+ - name: Invalidate GitHub App token
+ if: always() && steps.safe-outputs-app-token.outputs.token != ''
+ env:
+ TOKEN: ${{ steps.safe-outputs-app-token.outputs.token }}
+ run: |
+ echo "Revoking GitHub App installation token..."
+ # GitHub CLI will auth with the token being revoked.
+ gh api \
+ --method DELETE \
+ -H "Authorization: token $TOKEN" \
+ /installation/token || echo "Token revoke may already be expired."
+
+ echo "Token invalidation step complete."
safe_outputs:
needs: agent
@@ -1071,6 +997,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/artifacts-summary"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "artifacts-summary"
GH_AW_WORKFLOW_NAME: "Artifacts Summary"
outputs:
@@ -1091,7 +1018,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1105,6 +1032,18 @@ jobs:
mkdir -p /tmp/gh-aw/
find "/tmp/gh-aw/" -type f -print
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV"
+ - name: Generate GitHub App token
+ id: safe-outputs-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
+ permission-contents: read
+ permission-discussions: write
+ permission-issues: write
- name: Process Safe Outputs
id: process_safe_outputs
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1115,12 +1054,25 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"artifacts\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1},\"missing_data\":{},\"missing_tool\":{}}"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
+ - name: Invalidate GitHub App token
+ if: always() && steps.safe-outputs-app-token.outputs.token != ''
+ env:
+ TOKEN: ${{ steps.safe-outputs-app-token.outputs.token }}
+ run: |
+ echo "Revoking GitHub App installation token..."
+ # GitHub CLI will auth with the token being revoked.
+ gh api \
+ --method DELETE \
+ -H "Authorization: token $TOKEN" \
+ /installation/token || echo "Token revoke may already be expired."
+
+ echo "Token invalidation step complete."
- name: Upload Safe Output Items Manifest
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml
index cb4fbbe3ccb..7b7475b9714 100644
--- a/.github/workflows/audit-workflows.lock.yml
+++ b/.github/workflows/audit-workflows.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,18 +81,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -110,9 +110,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "audit-workflows.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -128,17 +128,18 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, upload_asset, missing_tool, missing_data, noop
@@ -174,6 +175,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -197,9 +199,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -224,10 +226,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -255,11 +257,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -285,10 +287,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: auditworkflows
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -309,7 +312,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -345,7 +348,11 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
- name: Setup Python environment
@@ -373,7 +380,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -390,7 +397,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -413,9 +420,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -423,7 +430,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -434,10 +441,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -452,198 +459,37 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -739,6 +585,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -763,8 +610,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -775,7 +622,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -786,7 +633,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -805,9 +653,9 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -819,16 +667,28 @@ jobs:
"GITHUB_TOKEN": "$GITHUB_TOKEN",
"GITHUB_ACTOR": "$GITHUB_ACTOR",
"GITHUB_REPOSITORY": "$GITHUB_REPOSITORY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -836,6 +696,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -853,7 +720,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -933,7 +801,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash,BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -981,15 +849,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -999,7 +867,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1016,9 +884,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1027,18 +895,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1135,9 +1003,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1170,7 +1038,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1198,9 +1066,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1247,6 +1115,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-audit-workflows"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1262,7 +1132,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1287,9 +1157,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1301,9 +1171,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1329,9 +1199,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1347,9 +1217,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1361,6 +1231,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1376,7 +1248,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1419,9 +1291,9 @@ jobs:
FILE_GLOB_FILTER: "memory/audit-workflows/*.json memory/audit-workflows/*.jsonl memory/audit-workflows/*.csv memory/audit-workflows/*.md"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1436,6 +1308,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/audit-workflows"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "audit-workflows-daily"
GH_AW_WORKFLOW_ID: "audit-workflows"
GH_AW_WORKFLOW_NAME: "Agentic Workflow Audit Agent"
@@ -1457,7 +1330,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1483,9 +1356,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1502,6 +1375,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: auditworkflows
steps:
- name: Checkout actions folder
@@ -1514,7 +1388,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1559,7 +1433,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1615,8 +1489,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/auto-triage-issues.lock.yml b/.github/workflows/auto-triage-issues.lock.yml
index eb54d5c99d0..287b198e9a5 100644
--- a/.github/workflows/auto-triage-issues.lock.yml
+++ b/.github/workflows/auto-triage-issues.lock.yml
@@ -72,7 +72,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -87,14 +87,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -111,18 +111,18 @@ jobs:
GH_AW_WORKFLOW_FILE: "auto-triage-issues.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/compute_text.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -137,15 +137,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, add_labels, missing_tool, missing_data, noop
@@ -179,6 +179,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -195,9 +196,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -214,10 +215,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -237,11 +238,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -264,10 +265,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: autotriageissues
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -289,13 +291,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -318,16 +324,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -336,181 +342,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_labels":{"max":10},"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[Auto-Triage] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Add labels to an existing GitHub issue or pull request for categorization and filtering. Labels must already exist in the repository. For creating new issues with labels, use create_issue with the labels property instead. CONSTRAINTS: Maximum 10 label(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). If omitted, adds labels to the issue or PR that triggered this workflow. Only works for issue or pull_request event triggers. For schedule, workflow_dispatch, or other triggers, item_number is required — omitting it will silently skip the label operation.",
- "type": "number"
- },
- "labels": {
- "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "add_labels"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_labels": " CONSTRAINTS: Maximum 10 label(s) can be added.",
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[Auto-Triage] \". Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_labels": {
"defaultMax": 5,
@@ -616,6 +472,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -640,8 +497,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -652,7 +509,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -660,7 +517,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -678,10 +536,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -689,10 +547,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "issues"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -700,6 +563,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -717,7 +587,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -742,7 +613,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq *)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -771,7 +642,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -809,15 +680,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -826,7 +697,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -838,14 +709,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -854,18 +725,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -940,9 +811,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -965,7 +836,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -993,9 +864,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1040,6 +911,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-auto-triage-issues"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1055,7 +928,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1079,9 +952,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1092,9 +965,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1115,9 +988,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1132,9 +1005,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
pre_activation:
@@ -1156,7 +1029,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1165,9 +1038,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check user rate limit
id: check_rate_limit
@@ -1180,9 +1053,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_rate_limit.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_rate_limit.cjs');
await main();
safe_outputs:
@@ -1198,6 +1071,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/auto-triage-issues"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "auto-triage-issues"
GH_AW_WORKFLOW_NAME: "Auto-Triage Issues"
outputs:
@@ -1218,7 +1092,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1237,16 +1111,16 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_labels\":{\"max\":10},\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[Auto-Triage] \"},\"missing_data\":{},\"missing_tool\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/blog-auditor.lock.yml b/.github/workflows/blog-auditor.lock.yml
index cf475fdef9b..cef9710252a 100644
--- a/.github/workflows/blog-auditor.lock.yml
+++ b/.github/workflows/blog-auditor.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","githubnext.com","www.githubnext.com"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "false"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "blog-auditor.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -126,16 +126,16 @@ jobs:
GH_AW_GITHUB_SERVER_URL: ${{ github.server_url }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/playwright_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/playwright_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -169,6 +169,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -188,9 +189,9 @@ jobs:
GH_AW_GITHUB_SERVER_URL: ${{ github.server_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -207,10 +208,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -230,11 +231,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -259,10 +260,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: blogauditor
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -283,13 +285,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -312,9 +318,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -322,7 +328,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -333,152 +339,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcr.microsoft.com/playwright/mcp node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcr.microsoft.com/playwright/mcp node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[audit] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[audit] \". Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -565,6 +449,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -589,8 +474,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -601,7 +486,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -609,7 +494,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -628,19 +514,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"playwright": {
@@ -658,13 +549,27 @@ jobs:
"/tmp/gh-aw/mcp-logs/playwright",
"--no-sandbox"
],
- "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"]
+ "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"safeoutputs": {
"type": "http",
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -682,7 +587,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -798,7 +704,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,githubnext.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.githubnext.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,githubnext.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.githubnext.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(cat *),Bash(cat),Bash(date *),Bash(date),Bash(echo *),Bash(echo),Bash(find * -maxdepth 1),Bash(gh aw compile *),Bash(grep),Bash(head),Bash(ls),Bash(mktemp *),Bash(pwd),Bash(rm *),Bash(sort),Bash(tail),Bash(test *),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users,mcp__playwright__browser_click,mcp__playwright__browser_close,mcp__playwright__browser_console_messages,mcp__playwright__browser_drag,mcp__playwright__browser_evaluate,mcp__playwright__browser_file_upload,mcp__playwright__browser_fill_form,mcp__playwright__browser_handle_dialog,mcp__playwright__browser_hover,mcp__playwright__browser_install,mcp__playwright__browser_navigate,mcp__playwright__browser_navigate_back,mcp__playwright__browser_network_requests,mcp__playwright__browser_press_key,mcp__playwright__browser_resize,mcp__playwright__browser_select_option,mcp__playwright__browser_snapshot,mcp__playwright__browser_tabs,mcp__playwright__browser_take_screenshot,mcp__playwright__browser_type,mcp__playwright__browser_wait_for'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -842,15 +748,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -860,7 +766,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -877,9 +783,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -888,18 +794,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -972,9 +878,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1007,7 +913,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1035,9 +941,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1081,6 +987,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-blog-auditor"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1096,7 +1004,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1121,9 +1029,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1135,9 +1043,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1159,9 +1067,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1177,9 +1085,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1194,6 +1102,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/blog-auditor"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "blog-auditor-weekly"
GH_AW_WORKFLOW_ID: "blog-auditor"
GH_AW_WORKFLOW_NAME: "Blog Auditor"
@@ -1215,7 +1124,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1241,9 +1150,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/bot-detection.lock.yml b/.github/workflows/bot-detection.lock.yml
index ba81ab6d150..344a8140c10 100644
--- a/.github/workflows/bot-detection.lock.yml
+++ b/.github/workflows/bot-detection.lock.yml
@@ -23,7 +23,7 @@
#
# Investigates suspicious repository activity and maintains a single triage issue
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"45b5b917f22b3ce4657e92078ab1e4c13bda07e7c2497a72f877acf95ead5941","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"f847d84805ff36145ce98c043742c4640b7d8a9ecb702994ee8ffdbfbdb49117","strict":true}
name: "Bot Detection"
"on":
@@ -61,7 +61,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -76,18 +76,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -105,9 +105,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "bot-detection.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -126,15 +126,15 @@ jobs:
GH_AW_NEEDS_PRECOMPUTE_OUTPUTS_ISSUE_NUMBER: ${{ needs.precompute.outputs.issue_number }}
GH_AW_NEEDS_PRECOMPUTE_OUTPUTS_ISSUE_TITLE: ${{ needs.precompute.outputs.issue_title }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, update_issue, missing_tool, missing_data, noop
@@ -168,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -186,9 +187,9 @@ jobs:
GH_AW_NEEDS_PRECOMPUTE_OUTPUTS_ISSUE_TITLE: ${{ needs.precompute.outputs.issue_title }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -208,10 +209,10 @@ jobs:
GH_AW_NEEDS_PRECOMPUTE_OUTPUTS_ISSUE_TITLE: ${{ needs.precompute.outputs.issue_title }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -234,11 +235,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -267,10 +268,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: botdetection
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -290,13 +292,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -319,16 +325,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -337,239 +343,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"max":1},"mentions":{"allowed":["pelikhan"]},"missing_data":{},"missing_tool":{},"noop":{"max":1},"update_issue":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Labels [\"security\" \"bot-detection\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Update an existing GitHub issue's title, body, labels, assignees, or milestone WITHOUT closing it. This tool is primarily for editing issue metadata and content. While it supports changing status between 'open' and 'closed', use close_issue instead when you want to close an issue with a closing comment. Body updates support replacing, appending to, prepending content, or updating a per-run \"island\" section. CONSTRAINTS: Maximum 1 issue(s) can be updated. Target: *. Body updates are allowed.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "assignees": {
- "description": "Replace the issue assignees with this list of GitHub usernames (e.g., ['octocat', 'mona']).",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "body": {
- "description": "Issue body content in Markdown. For 'replace', this becomes the entire body. For 'append'/'prepend', this content is added with a separator and an attribution footer. For 'replace-island', only the run-specific section is updated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "issue_number": {
- "description": "Issue number to update. This is the numeric ID from the GitHub URL (e.g., 789 in github.com/owner/repo/issues/789). Required when the workflow target is '*' (any issue).",
- "type": [
- "number",
- "string"
- ]
- },
- "labels": {
- "description": "Replace the issue labels with this list (e.g., ['bug', 'tracking:foo']). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "milestone": {
- "description": "Milestone number to assign (e.g., 1). Use null to clear.",
- "type": [
- "number",
- "string"
- ]
- },
- "operation": {
- "description": "How to update the issue body: 'append' (default - add to end with separator), 'prepend' (add to start with separator), 'replace' (overwrite entire body), or 'replace-island' (update a run-specific section).",
- "enum": [
- "replace",
- "append",
- "prepend",
- "replace-island"
- ],
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "status": {
- "description": "New issue status: 'open' to reopen a closed issue, 'closed' to close an open issue.",
- "enum": [
- "open",
- "closed"
- ],
- "type": "string"
- },
- "title": {
- "description": "New issue title to replace the existing title.",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "update_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Labels [\"security\" \"bot-detection\"] will be automatically added.",
+ "update_issue": " CONSTRAINTS: Maximum 1 issue(s) can be updated. Target: *. Body updates are allowed."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -717,6 +515,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -741,8 +540,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -753,7 +552,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -761,7 +560,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -779,10 +579,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -790,10 +590,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -801,6 +606,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -818,7 +630,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -827,7 +640,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -855,7 +668,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -893,15 +706,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -911,7 +724,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -928,9 +741,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -939,18 +752,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -998,6 +811,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-bot-detection"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1013,7 +828,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1037,9 +852,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1050,9 +865,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1072,9 +887,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1089,9 +904,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
precompute:
@@ -1112,7 +927,7 @@ jobs:
id: precompute
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
- github-token: ${{ secrets.GH_AW_BOT_DETECTION_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { owner, repo } = context.repo;
const HOURS_BACK = 6;
@@ -1903,6 +1718,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/bot-detection"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "bot-detection"
GH_AW_WORKFLOW_NAME: "Bot Detection"
outputs:
@@ -1925,7 +1741,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1951,9 +1767,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/bot-detection.md b/.github/workflows/bot-detection.md
index aaaea30773b..689ba25fed0 100644
--- a/.github/workflows/bot-detection.md
+++ b/.github/workflows/bot-detection.md
@@ -33,7 +33,7 @@ jobs:
id: precompute
uses: actions/github-script@v8
with:
- github-token: ${{ secrets.GH_AW_BOT_DETECTION_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { owner, repo } = context.repo;
const HOURS_BACK = 6;
diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml
index e7daf9d15c2..9a02b1ed63e 100644
--- a/.github/workflows/brave.lock.yml
+++ b/.github/workflows/brave.lock.yml
@@ -57,8 +57,9 @@ jobs:
pull-requests: write
outputs:
body: ${{ steps.sanitized.outputs.body }}
- comment_id: ""
- comment_repo: ""
+ comment_id: ${{ steps.add-comment.outputs.comment-id }}
+ comment_repo: ${{ steps.add-comment.outputs.comment-repo }}
+ comment_url: ${{ steps.add-comment.outputs.comment-url }}
model: ${{ steps.generate_aw_info.outputs.model }}
slash_command: ${{ needs.pre_activation.outputs.matched_command }}
text: ${{ steps.sanitized.outputs.text }}
@@ -74,7 +75,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -89,14 +90,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -116,9 +117,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -126,18 +127,31 @@ jobs:
GH_AW_WORKFLOW_FILE: "brave.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_WORKFLOW_NAME: "Brave Web Search Agent"
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🦁 *Search results brought to you by [{workflow_name}]({run_url})*{history_link}\",\"footerWorkflowRecompile\":\"\\u003e 🔄 *Maintenance report by [{workflow_name}]({run_url}) for {repository}*\",\"runStarted\":\"🔍 Brave Search activated! [{workflow_name}]({run_url}) is venturing into the web on this {event_type}...\",\"runSuccess\":\"🦁 Mission accomplished! [{workflow_name}]({run_url}) has returned with the findings. Knowledge acquired! 🏆\",\"runFailure\":\"🔍 Search interrupted! [{workflow_name}]({run_url}) {status}. The web remains unexplored...\"}"
+ with:
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_workflow_run_comment.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -155,15 +169,15 @@ jobs:
GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }}
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, missing_tool, missing_data, noop
@@ -197,8 +211,9 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
- cat "/opt/gh-aw/prompts/pr_context_prompt.md"
+ cat "${GH_AW_HOME}/prompts/pr_context_prompt.md"
fi
cat << 'GH_AW_PROMPT_EOF'
@@ -220,9 +235,9 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -243,10 +258,10 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -270,11 +285,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -298,10 +313,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: brave
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -323,13 +339,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -352,16 +372,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -370,155 +390,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh docker.io/mcp/brave-search ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh docker.io/mcp/brave-search ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -597,6 +492,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -621,8 +517,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -633,7 +529,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -642,7 +538,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -660,10 +557,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e BRAVE_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e BRAVE_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"brave-search": {
@@ -674,6 +571,13 @@ jobs:
],
"env": {
"BRAVE_API_KEY": "\${BRAVE_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
@@ -681,10 +585,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -692,6 +601,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -709,7 +625,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -718,7 +635,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -747,7 +664,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -785,15 +702,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'BRAVE_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -803,7 +720,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -821,9 +738,9 @@ jobs:
GH_AW_COMMAND: brave
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -832,18 +749,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -918,9 +835,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -943,7 +860,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -971,9 +888,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1018,6 +935,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-brave"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1033,7 +952,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1057,9 +976,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1070,9 +989,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1092,9 +1011,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1109,9 +1028,28 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
+ await main();
+ - name: Update reaction comment with completion status
+ id: conclusion
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ env.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: "Brave Web Search Agent"
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.agent.outputs.detection_conclusion }}
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🦁 *Search results brought to you by [{workflow_name}]({run_url})*{history_link}\",\"footerWorkflowRecompile\":\"\\u003e 🔄 *Maintenance report by [{workflow_name}]({run_url}) for {repository}*\",\"runStarted\":\"🔍 Brave Search activated! [{workflow_name}]({run_url}) is venturing into the web on this {event_type}...\",\"runSuccess\":\"🦁 Mission accomplished! [{workflow_name}]({run_url}) has returned with the findings. Knowledge acquired! 🏆\",\"runFailure\":\"🔍 Search interrupted! [{workflow_name}]({run_url}) {status}. The web remains unexplored...\"}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/notify_comment_error.cjs');
await main();
pre_activation:
@@ -1135,7 +1073,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for command workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1144,9 +1082,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check command position
id: check_command_position
@@ -1155,9 +1093,9 @@ jobs:
GH_AW_COMMANDS: "[\"brave\"]"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_command_position.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_command_position.cjs');
await main();
safe_outputs:
@@ -1173,6 +1111,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/brave"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🦁 *Search results brought to you by [{workflow_name}]({run_url})*{history_link}\",\"footerWorkflowRecompile\":\"\\u003e 🔄 *Maintenance report by [{workflow_name}]({run_url}) for {repository}*\",\"runStarted\":\"🔍 Brave Search activated! [{workflow_name}]({run_url}) is venturing into the web on this {event_type}...\",\"runSuccess\":\"🦁 Mission accomplished! [{workflow_name}]({run_url}) has returned with the findings. Knowledge acquired! 🏆\",\"runFailure\":\"🔍 Search interrupted! [{workflow_name}]({run_url}) {status}. The web remains unexplored...\"}"
GH_AW_WORKFLOW_ID: "brave"
GH_AW_WORKFLOW_NAME: "Brave Web Search Agent"
@@ -1196,7 +1135,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1222,9 +1161,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/breaking-change-checker.lock.yml b/.github/workflows/breaking-change-checker.lock.yml
index eecc25407d7..de9ea00f707 100644
--- a/.github/workflows/breaking-change-checker.lock.yml
+++ b/.github/workflows/breaking-change-checker.lock.yml
@@ -28,7 +28,7 @@
# - shared/activation-app.md
# - shared/reporting.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"d30dc29a4e3303a626094750fe8c3efecfbbe25ab6b39a475ab3217871047ed4","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"169a2c5b9a5969aa38426a524e77ed02c90711ca69ddda969edca4d7f1763d15","strict":true}
name: "Breaking Change Checker"
"on":
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,14 +81,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -105,9 +105,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "breaking-change-checker.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -122,15 +122,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -164,6 +164,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -186,9 +187,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -205,10 +206,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -228,11 +229,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -257,10 +258,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: breakingchangechecker
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -282,13 +284,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -311,16 +317,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -329,167 +335,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Assignees [\"copilot\"] will be automatically assigned.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Assignees [\"copilot\"] will be automatically assigned."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -583,6 +452,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -607,8 +477,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -619,7 +489,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -627,7 +497,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -645,10 +516,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -656,10 +527,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "repos"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -667,6 +543,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -684,7 +567,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -713,7 +597,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(cat:*)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(git diff:*)'\'' --allow-tool '\''shell(git log:*)'\'' --allow-tool '\''shell(git show:*)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(grep:*)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -742,7 +626,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -780,15 +664,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -797,7 +681,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -814,9 +698,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -825,18 +709,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -911,9 +795,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -936,7 +820,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -964,9 +848,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1009,6 +893,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-breaking-change-checker"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1024,7 +910,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1049,9 +935,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1063,9 +949,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1086,9 +972,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1104,9 +990,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
pre_activation:
@@ -1127,7 +1013,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1136,10 +1022,19 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
+ - name: Generate GitHub App token for skip-if checks
+ id: pre-activation-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
- name: Check skip-if-match query
id: check_skip_if_match
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1148,10 +1043,11 @@ jobs:
GH_AW_WORKFLOW_NAME: "Breaking Change Checker"
GH_AW_SKIP_MAX_MATCHES: "1"
with:
+ github-token: ${{ steps.pre-activation-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_skip_if_match.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_skip_if_match.cjs');
await main();
safe_outputs:
@@ -1165,6 +1061,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/breaking-change-checker"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e ⚠️ *Compatibility report by [{workflow_name}]({run_url})*{history_link}\",\"footerWorkflowRecompile\":\"\\u003e 🛠️ *Workflow maintenance by [{workflow_name}]({run_url}) for {repository}*\",\"runStarted\":\"🔬 Breaking Change Checker online! [{workflow_name}]({run_url}) is analyzing API compatibility on this {event_type}...\",\"runSuccess\":\"✅ Analysis complete! [{workflow_name}]({run_url}) has reviewed all changes. Compatibility verdict delivered! 📋\",\"runFailure\":\"🔬 Analysis interrupted! [{workflow_name}]({run_url}) {status}. Compatibility status unknown...\"}"
GH_AW_TRACKER_ID: "breaking-change-checker"
GH_AW_WORKFLOW_ID: "breaking-change-checker"
@@ -1189,7 +1086,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1216,9 +1113,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Assign Copilot to created issues
if: steps.process_safe_outputs.outputs.issues_to_assign_copilot != ''
@@ -1228,9 +1125,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/assign_copilot_to_created_issues.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/assign_copilot_to_created_issues.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml
index 16de54c9342..128d4f6aae1 100644
--- a/.github/workflows/changeset.lock.yml
+++ b/.github/workflows/changeset.lock.yml
@@ -29,7 +29,7 @@
# - shared/jqschema.md
# - shared/safe-output-app.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"a68f2edc9e5d35050adaf025a4b924895ab5ec5219264fd0237515b01c0fbb76","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"921e0b841efcd5abdf4dcf3f7ba790a0e68db29dd2973b91821044ad5bf491ef","strict":true}
name: "Changeset Generator"
"on":
@@ -82,7 +82,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -97,18 +97,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","node","go"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate CODEX_API_KEY or OPENAI_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
@@ -130,9 +130,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -140,18 +140,18 @@ jobs:
GH_AW_WORKFLOW_FILE: "changeset.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/compute_text.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -168,20 +168,20 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: update_pull_request, push_to_pull_request_branch, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_push_to_pr_branch.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_push_to_pr_branch.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -213,6 +213,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -239,9 +240,9 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -260,10 +261,10 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -285,11 +286,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -312,10 +313,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: changeset
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -336,13 +338,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
@@ -368,9 +374,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -380,7 +386,7 @@ jobs:
- name: Install Codex
run: npm install -g @openai/codex@latest
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -389,200 +395,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{"max":1},"update_pull_request":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Update an existing GitHub pull request's title or body. Supports replacing, appending to, or prepending content to the body. Title is always replaced. Only the fields you specify will be updated; other fields remain unchanged. CONSTRAINTS: Maximum 1 pull request(s) can be updated.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Pull request body content in Markdown. For 'replace', this becomes the entire body. For 'append'/'prepend', this is added with a separator.",
- "type": "string"
- },
- "draft": {
- "description": "Whether the PR should be a draft (true) or ready for review (false). Use to convert between draft and ready states.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "operation": {
- "description": "How to update the PR body: 'replace' (default - completely overwrite), 'append' (add to end with separator), or 'prepend' (add to start with separator). Title is always replaced.",
- "enum": [
- "replace",
- "append",
- "prepend"
- ],
- "type": "string"
- },
- "pull_request_number": {
- "description": "Pull request number to update. This is the numeric ID from the GitHub URL (e.g., 234 in github.com/owner/repo/pull/234). Required when the workflow target is '*' (any PR).",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "New pull request title to replace the existing title.",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "update_pull_request"
- },
- {
- "description": "Push committed changes to a pull request's branch. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. Changes must be committed locally before calling this tool.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "branch": {
- "description": "Branch name to push changes from. If omitted, uses the current working branch. Only specify if you need to push from a different branch.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Commit message describing the changes. Follow repository commit message conventions (e.g., conventional commits).",
- "type": "string"
- },
- "pull_request_number": {
- "description": "Pull request number to push changes to. This is the numeric ID from the GitHub URL (e.g., 654 in github.com/owner/repo/pull/654). Required when the workflow target is '*' (any PR).",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "push_to_pull_request_branch"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "update_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be updated."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"missing_data": {
"defaultMax": 20,
@@ -697,6 +533,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -721,8 +558,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -733,7 +570,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -741,7 +578,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -759,7 +597,7 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="codex"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
cat > /tmp/gh-aw/mcp-config/config.toml << GH_AW_MCP_CONFIG_EOF
[history]
@@ -783,20 +621,30 @@ jobs:
[mcp_servers.safeoutputs.headers]
Authorization = "$GH_AW_SAFE_OUTPUTS_API_KEY"
+
+ [mcp_servers.safeoutputs."guard-policies"]
+
+ [mcp_servers.safeoutputs."guard-policies".write-sink]
+ accept = ["*"]
GH_AW_MCP_CONFIG_EOF
# Generate JSON config for MCP gateway
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -804,6 +652,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -821,13 +676,14 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Codex
run: |
set -o pipefail
mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,172.30.0.1,api.npms.io,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,go.dev,golang.org,googleapis.deno.dev,googlechromelabs.github.io,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,sum.golang.org,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,172.30.0.1,api.npms.io,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,go.dev,golang.org,googleapis.deno.dev,googlechromelabs.github.io,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,sum.golang.org,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" && codex ${GH_AW_MODEL_AGENT_CODEX:+-c model="$GH_AW_MODEL_AGENT_CODEX" }exec -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
@@ -866,15 +722,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'CODEX_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY'
@@ -885,7 +741,7 @@ jobs:
SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -902,9 +758,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -913,18 +769,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_codex_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_codex_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1000,9 +856,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1016,9 +872,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1061,6 +917,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-changeset"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1076,7 +934,18 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
+ - name: Generate GitHub App token
+ id: safe-outputs-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
+ permission-contents: write
+ permission-pull-requests: write
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1098,11 +967,11 @@ jobs:
GH_AW_NOOP_MAX: "1"
GH_AW_WORKFLOW_NAME: "Changeset Generator"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1111,11 +980,11 @@ jobs:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
GH_AW_WORKFLOW_NAME: "Changeset Generator"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1134,11 +1003,11 @@ jobs:
GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
GH_AW_TIMEOUT_MINUTES: "20"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1151,12 +1020,25 @@ jobs:
GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }}
GH_AW_NOOP_REPORT_AS_ISSUE: "true"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
+ - name: Invalidate GitHub App token
+ if: always() && steps.safe-outputs-app-token.outputs.token != ''
+ env:
+ TOKEN: ${{ steps.safe-outputs-app-token.outputs.token }}
+ run: |
+ echo "Revoking GitHub App installation token..."
+ # GitHub CLI will auth with the token being revoked.
+ gh api \
+ --method DELETE \
+ -H "Authorization: token $TOKEN" \
+ /installation/token || echo "Token revoke may already be expired."
+
+ echo "Token invalidation step complete."
pre_activation:
if: >
@@ -1180,7 +1062,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1189,9 +1071,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
safe_outputs:
@@ -1208,6 +1090,7 @@ jobs:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/changeset"
GH_AW_ENGINE_ID: "codex"
GH_AW_ENGINE_MODEL: "gpt-5.1-codex-mini"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "changeset"
GH_AW_WORKFLOW_NAME: "Changeset Generator"
outputs:
@@ -1230,7 +1113,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1250,12 +1133,23 @@ jobs:
with:
name: agent
path: /tmp/gh-aw/
+ - name: Generate GitHub App token
+ id: safe-outputs-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
+ permission-contents: write
+ permission-pull-requests: write
- name: Checkout repository
if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch'))
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }}
- token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ token: ${{ steps.safe-outputs-app-token.outputs.token }}
persist-credentials: false
fetch-depth: 1
- name: Configure Git credentials
@@ -1263,7 +1157,7 @@ jobs:
env:
REPO_NAME: ${{ github.repository }}
SERVER_URL: ${{ github.server_url }}
- GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ GIT_TOKEN: ${{ steps.safe-outputs-app-token.outputs.token }}
run: |
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
@@ -1282,13 +1176,27 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"allowed_files\":[\".changeset/**\"],\"commit_title_suffix\":\" [skip-ci]\",\"if_no_changes\":\"warn\",\"max_patch_size\":1024,\"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\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\",\".codex/\"]},\"update_pull_request\":{\"allow_body\":true,\"allow_title\":false,\"default_operation\":\"append\",\"max\":1}}"
GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }}
+ GITHUB_TOKEN: ${{ steps.safe-outputs-app-token.outputs.token }}
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
+ - name: Invalidate GitHub App token
+ if: always() && steps.safe-outputs-app-token.outputs.token != ''
+ env:
+ TOKEN: ${{ steps.safe-outputs-app-token.outputs.token }}
+ run: |
+ echo "Revoking GitHub App installation token..."
+ # GitHub CLI will auth with the token being revoked.
+ gh api \
+ --method DELETE \
+ -H "Authorization: token $TOKEN" \
+ /installation/token || echo "Token revoke may already be expired."
+
+ echo "Token invalidation step complete."
- name: Upload Safe Output Items Manifest
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
diff --git a/.github/workflows/chroma-issue-indexer.lock.yml b/.github/workflows/chroma-issue-indexer.lock.yml
deleted file mode 100644
index 5019dcd1b97..00000000000
--- a/.github/workflows/chroma-issue-indexer.lock.yml
+++ /dev/null
@@ -1,548 +0,0 @@
-#
-# ___ _ _
-# / _ \ | | (_)
-# | |_| | __ _ ___ _ __ | |_ _ ___
-# | _ |/ _` |/ _ \ '_ \| __| |/ __|
-# | | | | (_| | __/ | | | |_| | (__
-# \_| |_/\__, |\___|_| |_|\__|_|\___|
-# __/ |
-# _ _ |___/
-# | | | | / _| |
-# | | | | ___ _ __ _ __| |_| | _____ ____
-# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___|
-# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
-# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
-#
-# This file was automatically generated by gh-aw. DO NOT EDIT.
-#
-# To update this file, edit the corresponding .md file and run:
-# gh aw compile
-# Not all edits will cause changes to this file.
-#
-# For more information: https://github.github.com/gh-aw/introduction/overview/
-#
-#
-# Resolved workflow manifest:
-# Imports:
-# - shared/mcp/chroma.md
-#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"dbc1d32a392c06506e26b62a1ca05966a17d9f802ce08e8f9fcad9e93603737f","strict":true}
-
-name: "Chroma Issue Indexer"
-"on":
- schedule:
- - cron: "0 */4 * * *"
- workflow_dispatch:
-
-permissions: {}
-
-concurrency:
- group: "gh-aw-${{ github.workflow }}"
-
-run-name: "Chroma Issue Indexer"
-
-jobs:
- activation:
- runs-on: ubuntu-slim
- permissions:
- contents: read
- outputs:
- comment_id: ""
- comment_repo: ""
- model: ${{ steps.generate_aw_info.outputs.model }}
- secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
- steps:
- - name: Checkout actions folder
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- with:
- repository: github/gh-aw
- sparse-checkout: |
- actions
- persist-credentials: false
- - name: Setup Scripts
- uses: ./actions/setup
- with:
- destination: /opt/gh-aw/actions
- - 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: "gpt-5.1-codex-mini"
- GH_AW_INFO_VERSION: ""
- GH_AW_INFO_AGENT_VERSION: "latest"
- GH_AW_INFO_WORKFLOW_NAME: "Chroma Issue Indexer"
- GH_AW_INFO_EXPERIMENTAL: "false"
- GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
- GH_AW_INFO_STAGED: "false"
- GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
- GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
- GH_AW_INFO_AWMG_VERSION: ""
- GH_AW_INFO_FIREWALL_TYPE: "squid"
- GH_AW_COMPILED_STRICT: "true"
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- with:
- script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
- await main(core, context);
- - name: Validate COPILOT_GITHUB_TOKEN secret
- id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
- env:
- COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- - name: Checkout .github and .agents folders
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- with:
- persist-credentials: false
- sparse-checkout: |
- .github
- .agents
- sparse-checkout-cone-mode: true
- fetch-depth: 1
- - name: Check workflow file timestamps
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_WORKFLOW_FILE: "chroma-issue-indexer.lock.yml"
- with:
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
- await main();
- - name: Create prompt with built-in context
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_GITHUB_ACTOR: ${{ github.actor }}
- GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }}
- GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }}
- GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
- GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
- GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
- GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
- run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
- {
- cat << 'GH_AW_PROMPT_EOF'
-
- GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt_multi.md"
- cat << 'GH_AW_PROMPT_EOF'
-
- The following GitHub context information is available for this workflow:
- {{#if __GH_AW_GITHUB_ACTOR__ }}
- - **actor**: __GH_AW_GITHUB_ACTOR__
- {{/if}}
- {{#if __GH_AW_GITHUB_REPOSITORY__ }}
- - **repository**: __GH_AW_GITHUB_REPOSITORY__
- {{/if}}
- {{#if __GH_AW_GITHUB_WORKSPACE__ }}
- - **workspace**: __GH_AW_GITHUB_WORKSPACE__
- {{/if}}
- {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }}
- - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__
- {{/if}}
- {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }}
- - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__
- {{/if}}
- {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }}
- - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__
- {{/if}}
- {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }}
- - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__
- {{/if}}
- {{#if __GH_AW_GITHUB_RUN_ID__ }}
- - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__
- {{/if}}
-
-
- GH_AW_PROMPT_EOF
- cat << 'GH_AW_PROMPT_EOF'
-
- GH_AW_PROMPT_EOF
- cat << 'GH_AW_PROMPT_EOF'
- {{#runtime-import .github/workflows/shared/mcp/chroma.md}}
- GH_AW_PROMPT_EOF
- cat << 'GH_AW_PROMPT_EOF'
- {{#runtime-import .github/workflows/chroma-issue-indexer.md}}
- GH_AW_PROMPT_EOF
- } > "$GH_AW_PROMPT"
- - name: Interpolate variables and render templates
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- with:
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
- await main();
- - name: Substitute placeholders
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_ALLOWED_EXTENSIONS: ''
- GH_AW_CACHE_EXAMPLES: "- `/tmp/gh-aw/cache-memory-chroma/notes.txt` - general notes and observations\n- `/tmp/gh-aw/cache-memory-chroma/notes.md` - markdown formatted notes\n- `/tmp/gh-aw/cache-memory-chroma/preferences.json` - user preferences and settings\n- `/tmp/gh-aw/cache-memory-chroma/history.jsonl` - activity history in JSON Lines format\n- `/tmp/gh-aw/cache-memory-chroma/data.csv` - tabular data\n- `/tmp/gh-aw/cache-memory-chroma/state/` - organized state files in subdirectories (with allowed file types)\n"
- GH_AW_CACHE_LIST: "- **chroma**: `/tmp/gh-aw/cache-memory-chroma/` - Persistent storage for Chroma vector database\n"
- GH_AW_GITHUB_ACTOR: ${{ github.actor }}
- GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }}
- GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }}
- GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
- GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
- GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
- GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
- with:
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
-
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
-
- // Call the substitution function
- return await substitutePlaceholders({
- file: process.env.GH_AW_PROMPT,
- substitutions: {
- GH_AW_ALLOWED_EXTENSIONS: process.env.GH_AW_ALLOWED_EXTENSIONS,
- GH_AW_CACHE_EXAMPLES: process.env.GH_AW_CACHE_EXAMPLES,
- GH_AW_CACHE_LIST: process.env.GH_AW_CACHE_LIST,
- GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
- GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID,
- GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER,
- GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER,
- GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER,
- GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
- GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
- GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE
- }
- });
- - name: Validate prompt placeholders
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
- - name: Print prompt
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
- - name: Upload activation artifact
- if: success()
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
- with:
- name: activation
- path: |
- /tmp/gh-aw/aw_info.json
- /tmp/gh-aw/aw-prompts/prompt.txt
- retention-days: 1
-
- agent:
- needs: activation
- runs-on: ubuntu-latest
- permissions:
- contents: read
- issues: read
- concurrency:
- group: "gh-aw-copilot-${{ github.workflow }}"
- env:
- GH_AW_WORKFLOW_ID_SANITIZED: chromaissueindexer
- outputs:
- checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
- inference_access_error: ${{ steps.detect-inference-error.outputs.inference_access_error || 'false' }}
- model: ${{ needs.activation.outputs.model }}
- steps:
- - name: Checkout actions folder
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- with:
- repository: github/gh-aw
- sparse-checkout: |
- actions
- persist-credentials: false
- - name: Setup Scripts
- uses: ./actions/setup
- with:
- destination: /opt/gh-aw/actions
- - name: Checkout repository
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- with:
- persist-credentials: false
- - name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
- # Cache memory file share configuration from frontmatter processed below
- - name: Create cache-memory directory (chroma)
- run: |
- mkdir -p /tmp/gh-aw/cache-memory-chroma
- - name: Cache cache-memory file share data (chroma)
- uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
- with:
- key: memory-chroma-${{ github.workflow }}-${{ github.run_id }}
- path: /tmp/gh-aw/cache-memory-chroma
- restore-keys: |
- memory-chroma-${{ github.workflow }}-
- - name: Configure Git credentials
- env:
- REPO_NAME: ${{ github.repository }}
- SERVER_URL: ${{ github.server_url }}
- run: |
- git config --global user.email "github-actions[bot]@users.noreply.github.com"
- git config --global user.name "github-actions[bot]"
- git config --global am.keepcr true
- # Re-authenticate git with GitHub token
- SERVER_URL_STRIPPED="${SERVER_URL#https://}"
- git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
- echo "Git configured with standard GitHub Actions identity"
- - name: Checkout PR branch
- id: checkout-pr
- if: |
- (github.event.pull_request) || (github.event.issue.pull_request)
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
- await main();
- - name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
- env:
- GH_HOST: github.com
- - name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
- - name: Determine automatic lockdown mode for GitHub MCP Server
- id: determine-automatic-lockdown
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
- GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
- with:
- script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
- await determineAutomaticLockdown(github, context, core);
- - name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcp/chroma
- - name: Start MCP Gateway
- id: start-mcp-gateway
- env:
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
- GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- run: |
- set -eo pipefail
- mkdir -p /tmp/gh-aw/mcp-config
-
- # Export gateway environment variables for MCP config and gateway script
- export MCP_GATEWAY_PORT="80"
- export MCP_GATEWAY_DOMAIN="host.docker.internal"
- MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
- echo "::add-mask::${MCP_GATEWAY_API_KEY}"
- export MCP_GATEWAY_API_KEY
- export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
- mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
- export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
- export DEBUG="*"
-
- export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -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 -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
-
- mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
- {
- "mcpServers": {
- "chroma": {
- "type": "stdio",
- "container": "mcp/chroma",
- "tools": [
- "chroma_list_collections",
- "chroma_create_collection",
- "chroma_peek_collection",
- "chroma_get_collection_info",
- "chroma_get_collection_count",
- "chroma_modify_collection",
- "chroma_delete_collection",
- "chroma_fork_collection",
- "chroma_add_documents",
- "chroma_query_documents",
- "chroma_get_documents",
- "chroma_update_documents",
- "chroma_delete_documents",
- "mcp_known_embedding_functions"
- ],
- "env": {
- "CHROMA_CLIENT_TYPE": "persistent",
- "CHROMA_DATA_DIR": "/tmp/gh-aw/cache-memory-chroma"
- }
- },
- "github": {
- "type": "stdio",
- "container": "ghcr.io/github/github-mcp-server:v0.32.0",
- "env": {
- "GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
- "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
- "GITHUB_READ_ONLY": "1",
- "GITHUB_TOOLSETS": "issues"
- }
- }
- },
- "gateway": {
- "port": $MCP_GATEWAY_PORT,
- "domain": "${MCP_GATEWAY_DOMAIN}",
- "apiKey": "${MCP_GATEWAY_API_KEY}",
- "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
- }
- }
- GH_AW_MCP_CONFIG_EOF
- - name: Download activation artifact
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: activation
- path: /tmp/gh-aw
- - name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
- - name: Execute GitHub Copilot CLI
- id: agentic_execution
- # Copilot CLI tool arguments (sorted):
- timeout-minutes: 20
- run: |
- set -o pipefail
- touch /tmp/gh-aw/agent-step-summary.md
- # shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
- -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory-chroma/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
- env:
- COPILOT_AGENT_RUNNER_TYPE: STANDALONE
- COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- COPILOT_MODEL: gpt-5.1-codex-mini
- GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
- GH_AW_PHASE: agent
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_VERSION: dev
- GITHUB_API_URL: ${{ github.api_url }}
- GITHUB_AW: true
- GITHUB_HEAD_REF: ${{ github.head_ref }}
- GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- GITHUB_REF_NAME: ${{ github.ref_name }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
- GITHUB_WORKSPACE: ${{ github.workspace }}
- GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_AUTHOR_NAME: github-actions[bot]
- GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_COMMITTER_NAME: github-actions[bot]
- XDG_CONFIG_HOME: /home/runner
- - name: Detect inference access error
- id: detect-inference-error
- if: always()
- continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
- - name: Configure Git credentials
- env:
- REPO_NAME: ${{ github.repository }}
- SERVER_URL: ${{ github.server_url }}
- run: |
- git config --global user.email "github-actions[bot]@users.noreply.github.com"
- git config --global user.name "github-actions[bot]"
- git config --global am.keepcr true
- # Re-authenticate git with GitHub token
- SERVER_URL_STRIPPED="${SERVER_URL#https://}"
- git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
- echo "Git configured with standard GitHub Actions identity"
- - name: Copy Copilot session state files to logs
- if: always()
- continue-on-error: true
- run: |
- # Copy Copilot session state files to logs folder for artifact collection
- # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them
- SESSION_STATE_DIR="$HOME/.copilot/session-state"
- LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs"
-
- if [ -d "$SESSION_STATE_DIR" ]; then
- echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR"
- mkdir -p "$LOGS_DIR"
- cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true
- echo "Session state files copied successfully"
- else
- echo "No session-state directory found at $SESSION_STATE_DIR"
- fi
- - 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 /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- - name: Redact secrets in logs
- if: always()
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- with:
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
- await main();
- env:
- GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
- SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
- SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
- SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- - name: Append agent step summary
- if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
- - name: Parse agent logs for step summary
- if: always()
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
- with:
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
- await main();
- - name: Parse MCP Gateway logs for step summary
- if: always()
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- with:
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
- await main();
- - name: Print firewall logs
- if: always()
- continue-on-error: true
- env:
- AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs
- run: |
- # Fix permissions on firewall logs so they can be uploaded as artifacts
- # AWF runs with sudo, creating files owned by root
- sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true
- # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
- if command -v awf &> /dev/null; then
- awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
- else
- echo 'AWF binary not installed, skipping firewall log summary'
- fi
- - name: Upload agent artifacts
- if: always()
- continue-on-error: true
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
- 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/sandbox/firewall/logs/
- /tmp/gh-aw/agent-stdio.log
- /tmp/gh-aw/agent/
- if-no-files-found: ignore
-
diff --git a/.github/workflows/chroma-issue-indexer.md b/.github/workflows/chroma-issue-indexer.md
deleted file mode 100644
index 8abbc96d0bb..00000000000
--- a/.github/workflows/chroma-issue-indexer.md
+++ /dev/null
@@ -1,138 +0,0 @@
----
-on:
- schedule:
- - cron: '0 */4 * * *' # Every 4 hours
- workflow_dispatch:
-engine:
- id: copilot
- model: gpt-5.1-codex-mini
-imports:
- - shared/mcp/chroma.md
-permissions:
- contents: read
- issues: read
-tools:
- github:
- mode: local
- read-only: true
- toolsets: [issues]
----
-
-# Chroma Issue Indexer
-
-This workflow indexes issues from the repository into a Chroma vector database for semantic search and duplicate detection.
-
-## Task
-
-Index the 100 most recent issues from the repository into the Chroma vector database:
-
-1. **Create Chroma Collection First**:
- - IMPORTANT: Check if the "issues" collection exists using `chroma_list_collections`
- - If it doesn't exist, create it using `chroma_create_collection` with:
- - Collection name: "issues"
- - Use default embedding function (omit embedding_function_name parameter)
-
-2. **Fetch Issues Using GitHub MCP Tools** (NOT Python scripts):
- - Use the `list_issues` tool from GitHub MCP server to fetch issues
- - Fetch issues in batches of 5 at a time using the `perPage: 5` parameter
- - Start with page 1, then page 2, page 3, etc. until you have 100 issues total
- - Include both open and closed issues (omit state parameter to get both)
- - Order by created date descending to get most recent first: `orderBy: "CREATED_AT"`, `direction: "DESC"`
- - For each issue, extract: number, title, body, state, createdAt, author.login, url
-
-3. **Index Issues in Batches**:
- - Process each batch of 5 issues immediately after fetching
- - For each batch, use `chroma_add_documents` to add all 5 issues at once
- - Use ID format: `issue-{issue_number}` (e.g., "issue-123")
- - Document content: `{title}\n\n{body}` (combine title and body)
- - If body is empty/null, use just the title as content
- - Include metadata for each issue:
- - `number`: Issue number (as string)
- - `title`: Issue title
- - `state`: Issue state (OPEN or CLOSED)
- - `author`: Issue author username
- - `created_at`: Issue creation date (ISO 8601 format)
- - `url`: Issue URL
-
-4. **Report Progress**:
- - After processing all batches, use `chroma_get_collection_count` to get total issue count
- - Report how many issues were successfully indexed
- - Note any issues that couldn't be indexed (e.g., API errors)
-
-## Important Notes
-
-- **MUST use GitHub MCP tools** (`list_issues` tool), NOT Python scripts or `gh` CLI
-- **MUST create collection first** before attempting to add documents
-- Process exactly 5 issues per batch using `perPage: 5` and incrementing page number
-- Skip duplicate issues (Chroma will update if ID exists)
-- The collection persists in `/tmp/gh-aw/cache-memory-chroma/` across runs
-- This helps other workflows search for similar issues using semantic search
-
-
diff --git a/.github/workflows/ci-coach.lock.yml b/.github/workflows/ci-coach.lock.yml
index 54840ba8277..8324746441a 100644
--- a/.github/workflows/ci-coach.lock.yml
+++ b/.github/workflows/ci-coach.lock.yml
@@ -30,7 +30,7 @@
# - shared/ci-data-analysis.md
# - shared/reporting.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"88b91d779a358a487e2e8fd69378db2f2253396556fe3b7b0166d9f75e613e13","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"3b12b7582c326a0ce05ef31dd48f47f86e051e4caa78834f2a634a4d220247be","strict":true}
name: "CI Optimization Coach"
"on":
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,14 +80,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -104,9 +104,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "ci-coach.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -122,21 +122,21 @@ jobs:
GH_AW_GITHUB_RUN_NUMBER: ${{ github.run_number }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -168,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -195,9 +196,9 @@ jobs:
GH_AW_GITHUB_RUN_NUMBER: ${{ github.run_number }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -217,10 +218,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -243,11 +244,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -274,10 +275,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: cicoach
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -299,7 +301,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -312,7 +314,11 @@ jobs:
cache-dependency-path: 'actions/setup/js/package-lock.json'
package-manager-cache: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
name: Download CI workflow runs from last 7 days
@@ -347,7 +353,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -377,16 +383,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -395,167 +401,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request":{"expires":48,"max":1,"title_prefix":"[ci-coach] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[ci-coach] \".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[ci-coach] \"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -652,6 +521,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -676,8 +546,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -688,7 +558,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -696,7 +566,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -714,10 +585,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -725,10 +596,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -736,6 +612,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -753,7 +636,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -762,7 +646,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -791,7 +675,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -829,15 +713,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -846,7 +730,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -863,9 +747,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -874,18 +758,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -967,9 +851,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -992,7 +876,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1020,9 +904,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1067,6 +951,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-ci-coach"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1082,7 +968,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1107,9 +993,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1121,9 +1007,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1145,9 +1031,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1163,9 +1049,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1178,9 +1064,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
safe_outputs:
@@ -1197,6 +1083,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/ci-coach"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "ci-coach-daily"
GH_AW_WORKFLOW_ID: "ci-coach"
GH_AW_WORKFLOW_NAME: "CI Optimization Coach"
@@ -1220,7 +1107,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1270,14 +1157,14 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"expires\":48,\"max\":1,\"max_patch_size\":1024,\"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\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"title_prefix\":\"[ci-coach] \"},\"missing_data\":{},\"missing_tool\":{}}"
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"expires\":48,\"max\":1,\"max_patch_size\":1024,\"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\",\"AGENTS.md\"],\"protected_files_policy\":\"fallback-to-issue\",\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"title_prefix\":\"[ci-coach] \"},\"missing_data\":{},\"missing_tool\":{}}"
GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }}
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1294,6 +1181,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: cicoach
steps:
- name: Checkout actions folder
@@ -1306,7 +1194,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/ci-coach.md b/.github/workflows/ci-coach.md
index 111ae723e08..47b06a9bbd8 100644
--- a/.github/workflows/ci-coach.md
+++ b/.github/workflows/ci-coach.md
@@ -19,6 +19,7 @@ safe-outputs:
create-pull-request:
expires: 2d
title-prefix: "[ci-coach] "
+ protected-files: fallback-to-issue
timeout-minutes: 30
imports:
- shared/ci-data-analysis.md
diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml
index 4f6cf376978..a4c1373bd4a 100644
--- a/.github/workflows/ci-doctor.lock.yml
+++ b/.github/workflows/ci-doctor.lock.yml
@@ -74,7 +74,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -89,18 +89,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -118,9 +118,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "ci-doctor.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -141,16 +141,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, create_issue, update_issue, missing_tool, missing_data, noop
@@ -184,6 +184,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -204,9 +205,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -232,10 +233,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -264,11 +265,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -294,10 +295,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: cidoctor
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -319,13 +321,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
@@ -335,7 +341,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -365,16 +371,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -383,276 +389,32 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":1},"create_issue":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"update_issue":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[CI Failure Doctor] \". Labels [\"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Update an existing GitHub issue's title, body, labels, assignees, or milestone WITHOUT closing it. This tool is primarily for editing issue metadata and content. While it supports changing status between 'open' and 'closed', use close_issue instead when you want to close an issue with a closing comment. Body updates support replacing, appending to, prepending content, or updating a per-run \"island\" section. CONSTRAINTS: Maximum 1 issue(s) can be updated.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "assignees": {
- "description": "Replace the issue assignees with this list of GitHub usernames (e.g., ['octocat', 'mona']).",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "body": {
- "description": "Issue body content in Markdown. For 'replace', this becomes the entire body. For 'append'/'prepend', this content is added with a separator and an attribution footer. For 'replace-island', only the run-specific section is updated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "issue_number": {
- "description": "Issue number to update. This is the numeric ID from the GitHub URL (e.g., 789 in github.com/owner/repo/issues/789). Required when the workflow target is '*' (any issue).",
- "type": [
- "number",
- "string"
- ]
- },
- "labels": {
- "description": "Replace the issue labels with this list (e.g., ['bug', 'tracking:foo']). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "milestone": {
- "description": "Milestone number to assign (e.g., 1). Use null to clear.",
- "type": [
- "number",
- "string"
- ]
- },
- "operation": {
- "description": "How to update the issue body: 'append' (default - add to end with separator), 'prepend' (add to start with separator), 'replace' (overwrite entire body), or 'replace-island' (update a run-specific section).",
- "enum": [
- "replace",
- "append",
- "prepend",
- "replace-island"
- ],
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "status": {
- "description": "New issue status: 'open' to reopen a closed issue, 'closed' to close an open issue.",
- "enum": [
- "open",
- "closed"
- ],
- "type": "string"
- },
- "title": {
- "description": "New issue title to replace the existing title.",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "update_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added.",
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[CI Failure Doctor] \". Labels [\"cookie\"] will be automatically added.",
+ "update_issue": " CONSTRAINTS: Maximum 1 issue(s) can be updated."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -818,6 +580,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -842,8 +605,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -854,7 +617,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -862,7 +625,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -880,10 +644,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -891,10 +655,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,actions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -902,6 +671,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -919,7 +695,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -928,7 +705,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -956,7 +733,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -994,15 +771,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -1012,7 +789,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1029,9 +806,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1040,18 +817,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1132,9 +909,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1157,7 +934,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1184,9 +961,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1232,6 +1009,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-ci-doctor"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1247,7 +1026,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1273,9 +1052,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1288,9 +1067,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1313,9 +1092,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1332,9 +1111,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
pre_activation:
@@ -1356,7 +1135,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1365,9 +1144,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check stop-time limit
id: check_stop_time
@@ -1377,9 +1156,9 @@ jobs:
GH_AW_WORKFLOW_NAME: "CI Failure Doctor"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_stop_time.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_stop_time.cjs');
await main();
safe_outputs:
@@ -1396,6 +1175,7 @@ jobs:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/ci-doctor"
GH_AW_ENGINE_ID: "copilot"
GH_AW_ENGINE_MODEL: "gpt-5.1-codex-mini"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🩺 *Diagnosis provided by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🏥 CI Doctor reporting for duty! [{workflow_name}]({run_url}) is examining the patient on this {event_type}...\",\"runSuccess\":\"🩺 Examination complete! [{workflow_name}]({run_url}) has delivered the diagnosis. Prescription issued! 💊\",\"runFailure\":\"🏥 Medical emergency! [{workflow_name}]({run_url}) {status}. Doctor needs assistance...\"}"
GH_AW_WORKFLOW_ID: "ci-doctor"
GH_AW_WORKFLOW_NAME: "CI Failure Doctor"
@@ -1423,7 +1203,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1449,9 +1229,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1468,6 +1248,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: cidoctor
steps:
- name: Checkout actions folder
@@ -1480,7 +1261,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/claude-code-user-docs-review.lock.yml b/.github/workflows/claude-code-user-docs-review.lock.yml
index 265f44a2fcb..4b02abb3c33 100644
--- a/.github/workflows/claude-code-user-docs-review.lock.yml
+++ b/.github/workflows/claude-code-user-docs-review.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "claude-code-user-docs-review.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,16 +125,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -168,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -187,9 +188,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -208,10 +209,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -233,11 +234,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -264,10 +265,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: claudecodeuserdocsreview
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -288,16 +290,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -327,9 +333,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -337,7 +343,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -348,152 +354,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -580,6 +464,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -604,8 +489,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -616,7 +501,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -624,7 +509,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -642,19 +528,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,discussions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -662,6 +553,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -679,7 +577,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -759,7 +658,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash,BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -803,15 +702,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -821,7 +720,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -833,14 +732,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -849,18 +748,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -939,9 +838,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -974,7 +873,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1002,9 +901,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1049,6 +948,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-claude-code-user-docs-review"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1064,7 +965,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1089,9 +990,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1103,9 +1004,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1127,9 +1028,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1145,9 +1046,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1162,6 +1063,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/claude-code-user-docs-review"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "claude-code-user-docs-review"
GH_AW_WORKFLOW_ID: "claude-code-user-docs-review"
GH_AW_WORKFLOW_NAME: "Claude Code User Documentation Review"
@@ -1183,7 +1085,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1202,16 +1104,16 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1},\"missing_data\":{},\"missing_tool\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1228,6 +1130,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: claudecodeuserdocsreview
steps:
- name: Checkout actions folder
@@ -1240,7 +1143,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/cli-consistency-checker.lock.yml b/.github/workflows/cli-consistency-checker.lock.yml
index 0e42842c6fc..568e6629cb1 100644
--- a/.github/workflows/cli-consistency-checker.lock.yml
+++ b/.github/workflows/cli-consistency-checker.lock.yml
@@ -58,7 +58,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -73,14 +73,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","node","api.github.com","proxy.golang.org","sum.golang.org"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "false"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -97,9 +97,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "cli-consistency-checker.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -114,15 +114,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -156,6 +156,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -171,9 +172,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -189,10 +190,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -211,11 +212,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -242,10 +243,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: cliconsistencychecker
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -267,13 +269,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -296,16 +302,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -314,167 +320,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":48,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[cli-consistency] \". Labels [\"automation\" \"cli\" \"documentation\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[cli-consistency] \". Labels [\"automation\" \"cli\" \"documentation\" \"cookie\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -568,6 +437,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -592,8 +462,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -604,7 +474,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -612,7 +482,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -630,10 +501,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -641,10 +512,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -652,6 +528,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -669,7 +552,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -678,7 +562,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -707,7 +591,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -745,15 +629,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -762,7 +646,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -779,9 +663,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -790,18 +674,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -876,9 +760,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -901,7 +785,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -929,9 +813,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -974,6 +858,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-cli-consistency-checker"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -989,7 +875,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1013,9 +899,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1026,9 +912,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1047,9 +933,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1064,9 +950,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1080,6 +966,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/cli-consistency-checker"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "cli-consistency-checker"
GH_AW_WORKFLOW_NAME: "CLI Consistency Checker"
outputs:
@@ -1102,7 +989,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1128,9 +1015,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml
index e84fd7162ec..ac8fed6a679 100644
--- a/.github/workflows/cli-version-checker.lock.yml
+++ b/.github/workflows/cli-version-checker.lock.yml
@@ -21,14 +21,14 @@
#
# For more information: https://github.github.com/gh-aw/introduction/overview/
#
-# Monitors and updates agentic CLI tools (Claude Code, GitHub Copilot CLI, OpenAI Codex, GitHub MCP Server, Playwright MCP, Playwright Browser, MCP Gateway) for new versions
+# Monitors and updates agentic CLI tools (Claude Code, GitHub Copilot CLI, OpenAI Codex, GitHub MCP Server, Playwright MCP, Playwright Browser, MCP Gateway, APM) for new versions
#
# Resolved workflow manifest:
# Imports:
# - shared/jqschema.md
# - shared/reporting.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"f1edaf6fb88a3f4f73256faa6a1c60181f239cbc5e01436d07f90b282d6bff79"}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"e33a1982ca40e184f0ac66603487475e34c5357114f7bb3fe75d9ff92b3df4b6"}
name: "CLI Version Checker"
"on":
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,18 +80,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","node","go","api.github.com","ghcr.io"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "false"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -109,9 +109,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "cli-version-checker.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -126,16 +126,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -169,6 +169,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -190,9 +191,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -211,10 +212,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -236,11 +237,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -265,10 +266,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: cliversionchecker
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -289,19 +291,23 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -331,9 +337,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -341,7 +347,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -352,167 +358,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":48,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[ca] \". Labels [\"automation\" \"dependencies\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[ca] \". Labels [\"automation\" \"dependencies\" \"cookie\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -606,6 +475,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -630,8 +500,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -642,7 +512,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -650,7 +520,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -668,19 +539,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -688,6 +564,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -705,7 +588,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -786,7 +670,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.jsr.io,anthropic.com,api.anthropic.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,go.dev,golang.org,googleapis.deno.dev,googlechromelabs.github.io,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,skimdb.npmjs.com,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.jsr.io,anthropic.com,api.anthropic.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,go.dev,golang.org,googleapis.deno.dev,googlechromelabs.github.io,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,skimdb.npmjs.com,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash,BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,WebFetch,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -830,15 +714,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -848,7 +732,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -865,9 +749,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -876,18 +760,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -962,13 +846,13 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
WORKFLOW_NAME: "CLI Version Checker"
- WORKFLOW_DESCRIPTION: "Monitors and updates agentic CLI tools (Claude Code, GitHub Copilot CLI, OpenAI Codex, GitHub MCP Server, Playwright MCP, Playwright Browser, MCP Gateway) for new versions"
+ WORKFLOW_DESCRIPTION: "Monitors and updates agentic CLI tools (Claude Code, GitHub Copilot CLI, OpenAI Codex, GitHub MCP Server, Playwright MCP, Playwright Browser, MCP Gateway, APM) for new versions"
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1001,7 +885,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1029,9 +913,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1075,6 +959,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-cli-version-checker"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1090,7 +976,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1114,9 +1000,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1127,9 +1013,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1148,9 +1034,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1165,9 +1051,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1181,6 +1067,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/cli-version-checker"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "cli-version-checker"
GH_AW_WORKFLOW_NAME: "CLI Version Checker"
outputs:
@@ -1203,7 +1090,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1229,9 +1116,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1248,6 +1135,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: cliversionchecker
steps:
- name: Checkout actions folder
@@ -1260,7 +1148,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/cli-version-checker.md b/.github/workflows/cli-version-checker.md
index 44401a72e50..9f46e3ad030 100644
--- a/.github/workflows/cli-version-checker.md
+++ b/.github/workflows/cli-version-checker.md
@@ -1,5 +1,5 @@
---
-description: Monitors and updates agentic CLI tools (Claude Code, GitHub Copilot CLI, OpenAI Codex, GitHub MCP Server, Playwright MCP, Playwright Browser, MCP Gateway) for new versions
+description: Monitors and updates agentic CLI tools (Claude Code, GitHub Copilot CLI, OpenAI Codex, GitHub MCP Server, Playwright MCP, Playwright Browser, MCP Gateway, APM) for new versions
on:
schedule: daily
workflow_dispatch:
@@ -31,7 +31,7 @@ timeout-minutes: 45
# CLI Version Checker
-Monitor and update agentic CLI tools: Claude Code, GitHub Copilot CLI, OpenAI Codex, GitHub MCP Server, Playwright MCP, Playwright Browser, and MCP Gateway.
+Monitor and update agentic CLI tools: Claude Code, GitHub Copilot CLI, OpenAI Codex, GitHub MCP Server, Playwright MCP, Playwright Browser, MCP Gateway, and APM (Agent Package Manager).
**Repository**: ${{ github.repository }} | **Run**: ${{ github.run_id }}
@@ -74,6 +74,11 @@ For each CLI/MCP server:
- Release Notes: https://github.com/github/gh-aw-mcpg/releases
- Docker Image: `ghcr.io/github/gh-aw-mcpg:v{VERSION}`
- Used as default sandbox.agent container (see `pkg/constants/constants.go`)
+- **APM (Agent Package Manager)**: `https://api.github.com/repos/microsoft/APM/releases/latest`
+ - Repository: https://github.com/microsoft/APM
+ - Release Notes: https://github.com/microsoft/APM/releases
+ - Pinned via `DefaultAPMVersion` constant in `pkg/constants/constants.go`
+ - Used as the `version:` input in generated `microsoft/apm-action` steps
**Optimization**: Fetch all versions in parallel using multiple npm view or WebFetch calls in a single turn.
@@ -119,6 +124,9 @@ For each update, analyze intermediate versions:
- Parse release body for changelog entries
- **CRITICAL**: Convert PR/issue references to full URLs (e.g., `https://github.com/github/gh-aw-mcpg/pull/123`)
- Note: Used as default sandbox.agent container in MCP Gateway configuration
+- **APM**: Fetch release notes from https://github.com/microsoft/APM/releases/tag/{VERSION}
+ - Parse release body for changelog entries
+ - **CRITICAL**: Convert PR/issue references to full URLs (e.g., `https://github.com/microsoft/APM/pull/123`)
**NPM Metadata Fallback**: When GitHub release notes are unavailable, use:
- `npm view --json` for package metadata
@@ -267,6 +275,7 @@ Legacy template reference (adapt to use Report Structure Pattern above):
- GitHub MCP Server: Always fetch from https://github.com/github/github-mcp-server/releases
- Playwright Browser: Always fetch from https://github.com/microsoft/playwright/releases
- MCP Gateway: Always fetch from https://github.com/github/gh-aw-mcpg/releases
+ - APM: Always fetch from https://github.com/microsoft/APM/releases
- Copilot CLI: Try to fetch, but may be inaccessible (private repo)
- Playwright MCP: Check NPM metadata, uses Playwright versioning
- **EXPLORE SUBCOMMANDS**: Install and test CLI tools to discover new features via `--help` and explore each subcommand
diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml
index 05ad6bbc540..7a80acce9d4 100644
--- a/.github/workflows/cloclo.lock.yml
+++ b/.github/workflows/cloclo.lock.yml
@@ -27,7 +27,7 @@
# - shared/jqschema.md
# - shared/mcp/serena-go.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"183df887ed669775015161c11ab4c4b3f52f06a00a94ec9d64785a0eda3be9c2","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"ad111cf099d958c340ed839a8e88f5053a501ce37fb239bdc2645ee1a872d14a","strict":true}
name: "/cloclo"
"on":
@@ -35,6 +35,7 @@ name: "/cloclo"
types:
- created
- edited
+ - labeled
discussion_comment:
types:
- created
@@ -44,15 +45,17 @@ name: "/cloclo"
- created
- edited
issues:
- # names: # Label filtering applied via job conditions
- # - cloclo # Label filtering applied via job conditions
types:
+ - opened
+ - edited
+ - reopened
- labeled
pull_request:
types:
- opened
- edited
- reopened
+ - labeled
pull_request_review_comment:
types:
- created
@@ -70,9 +73,7 @@ jobs:
activation:
needs: pre_activation
if: >
- (needs.pre_activation.outputs.activated == 'true') && ((((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, '/cloclo ')) ||
+ (needs.pre_activation.outputs.activated == 'true') && (((github.event_name == 'issues') && ((startsWith(github.event.issue.body, '/cloclo ')) ||
(github.event.issue.body == '/cloclo')) || (github.event_name == 'issue_comment') && (((startsWith(github.event.comment.body, '/cloclo ')) ||
(github.event.comment.body == '/cloclo')) && (github.event.issue.pull_request == null)) || (github.event_name == 'issue_comment') &&
(((startsWith(github.event.comment.body, '/cloclo ')) || (github.event.comment.body == '/cloclo')) &&
@@ -82,10 +83,9 @@ jobs:
((startsWith(github.event.pull_request.body, '/cloclo ')) || (github.event.pull_request.body == '/cloclo')) ||
(github.event_name == 'discussion') && ((startsWith(github.event.discussion.body, '/cloclo ')) || (github.event.discussion.body == '/cloclo')) ||
(github.event_name == 'discussion_comment') && ((startsWith(github.event.comment.body, '/cloclo ')) ||
- (github.event.comment.body == '/cloclo')))) || (!(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') || ((github.event.action != 'labeled') ||
- (github.event.label.name == 'cloclo'))))
+ (github.event.comment.body == '/cloclo'))) || ((github.event_name == 'issues') && (github.event.label.name == 'cloclo') ||
+ (github.event_name == 'pull_request') && (github.event.label.name == 'cloclo') || (github.event_name == 'discussion') &&
+ (github.event.label.name == 'cloclo')))
runs-on: ubuntu-slim
permissions:
contents: read
@@ -97,6 +97,7 @@ jobs:
comment_id: ${{ steps.add-comment.outputs.comment-id }}
comment_repo: ${{ steps.add-comment.outputs.comment-repo }}
comment_url: ${{ steps.add-comment.outputs.comment-url }}
+ label_command: ${{ steps.remove_trigger_label.outputs.label_name }}
model: ${{ steps.generate_aw_info.outputs.model }}
secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
slash_command: ${{ needs.pre_activation.outputs.matched_command }}
@@ -113,7 +114,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -128,18 +129,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -160,9 +161,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -170,18 +171,18 @@ jobs:
GH_AW_WORKFLOW_FILE: "cloclo.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/compute_text.cjs');
await main();
- name: Add comment with workflow run link
id: add-comment
@@ -192,9 +193,20 @@ jobs:
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🎤 *Magnifique! Performance by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🎵 Comme d'habitude! [{workflow_name}]({run_url}) takes the stage on this {event_type}...\",\"runSuccess\":\"🎤 Bravo! [{workflow_name}]({run_url}) has delivered a stunning performance! Standing ovation! 🌟\",\"runFailure\":\"🎵 Intermission... [{workflow_name}]({run_url}) {status}. The show must go on... eventually!\"}"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_workflow_run_comment.cjs');
+ await main();
+ - name: Remove trigger label
+ id: remove_trigger_label
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_LABEL_NAMES: '["cloclo"]'
+ with:
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_workflow_run_comment.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/remove_trigger_label.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -215,22 +227,23 @@ jobs:
GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }}
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/playwright_prompt.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/playwright_prompt.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -262,8 +275,9 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
- cat "/opt/gh-aw/prompts/pr_context_prompt.md"
+ cat "${GH_AW_HOME}/prompts/pr_context_prompt.md"
fi
cat << 'GH_AW_PROMPT_EOF'
@@ -294,9 +308,9 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -323,10 +337,10 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -356,11 +370,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -385,10 +399,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: cloclo
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -409,7 +424,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -445,13 +460,17 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -481,9 +500,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -491,7 +510,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -502,10 +521,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest mcr.microsoft.com/playwright/mcp node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest mcr.microsoft.com/playwright/mcp node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -520,210 +539,37 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":1},"create_pull_request":{"expires":48,"max":1,"title_prefix":"[cloclo] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[cloclo] \". Labels [\"automation\" \"cloclo\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added.",
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[cloclo] \". Labels [\"automation\" \"cloclo\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -838,6 +684,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -862,8 +709,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -874,7 +721,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -882,7 +729,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -902,9 +750,9 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -916,16 +764,28 @@ jobs:
"GITHUB_TOKEN": "$GITHUB_TOKEN",
"GITHUB_ACTOR": "$GITHUB_ACTOR",
"GITHUB_REPOSITORY": "$GITHUB_REPOSITORY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"playwright": {
@@ -943,13 +803,27 @@ jobs:
"/tmp/gh-aw/mcp-logs/playwright",
"--no-sandbox"
],
- "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"]
+ "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"safeoutputs": {
"type": "http",
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -966,7 +840,14 @@ jobs:
"--project",
"\${GITHUB_WORKSPACE}"
],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -983,7 +864,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -1106,7 +988,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --max-turns 100 --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(/tmp/gh-aw/jqschema.sh),Bash(cat),Bash(date),Bash(echo),Bash(git add:*),Bash(git branch:*),Bash(git checkout:*),Bash(git commit:*),Bash(git merge:*),Bash(git rm:*),Bash(git status),Bash(git switch:*),Bash(git),Bash(grep),Bash(head),Bash(jq *),Bash(ls),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users,mcp__playwright__browser_click,mcp__playwright__browser_close,mcp__playwright__browser_console_messages,mcp__playwright__browser_drag,mcp__playwright__browser_evaluate,mcp__playwright__browser_file_upload,mcp__playwright__browser_fill_form,mcp__playwright__browser_handle_dialog,mcp__playwright__browser_hover,mcp__playwright__browser_install,mcp__playwright__browser_navigate,mcp__playwright__browser_navigate_back,mcp__playwright__browser_network_requests,mcp__playwright__browser_press_key,mcp__playwright__browser_resize,mcp__playwright__browser_select_option,mcp__playwright__browser_snapshot,mcp__playwright__browser_tabs,mcp__playwright__browser_take_screenshot,mcp__playwright__browser_type,mcp__playwright__browser_wait_for'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1151,15 +1033,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -1169,7 +1051,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1187,9 +1069,9 @@ jobs:
GH_AW_COMMAND: cloclo
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1198,18 +1080,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1289,9 +1171,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1324,7 +1206,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1352,9 +1234,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1400,6 +1282,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-cloclo"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1415,7 +1299,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1439,9 +1323,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1452,9 +1336,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1476,9 +1360,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1493,9 +1377,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1507,9 +1391,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
- name: Update reaction comment with completion status
id: conclusion
@@ -1526,15 +1410,13 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/notify_comment_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/notify_comment_error.cjs');
await main();
pre_activation:
if: >
- (((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, '/cloclo ')) || (github.event.issue.body == '/cloclo')) ||
(github.event_name == 'issue_comment') && (((startsWith(github.event.comment.body, '/cloclo ')) || (github.event.comment.body == '/cloclo')) &&
(github.event.issue.pull_request == null)) || (github.event_name == 'issue_comment') && (((startsWith(github.event.comment.body, '/cloclo ')) ||
@@ -1544,10 +1426,9 @@ jobs:
((startsWith(github.event.pull_request.body, '/cloclo ')) || (github.event.pull_request.body == '/cloclo')) ||
(github.event_name == 'discussion') && ((startsWith(github.event.discussion.body, '/cloclo ')) || (github.event.discussion.body == '/cloclo')) ||
(github.event_name == 'discussion_comment') && ((startsWith(github.event.comment.body, '/cloclo ')) ||
- (github.event.comment.body == '/cloclo')))) || (!(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') || ((github.event.action != 'labeled') ||
- (github.event.label.name == 'cloclo')))
+ (github.event.comment.body == '/cloclo'))) || ((github.event_name == 'issues') && (github.event.label.name == 'cloclo') ||
+ (github.event_name == 'pull_request') && (github.event.label.name == 'cloclo') || (github.event_name == 'discussion') &&
+ (github.event.label.name == 'cloclo'))
runs-on: ubuntu-slim
permissions:
contents: read
@@ -1565,7 +1446,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for command workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1574,9 +1455,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check command position
id: check_command_position
@@ -1585,9 +1466,9 @@ jobs:
GH_AW_COMMANDS: "[\"cloclo\"]"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_command_position.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_command_position.cjs');
await main();
safe_outputs:
@@ -1605,6 +1486,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/cloclo"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🎤 *Magnifique! Performance by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🎵 Comme d'habitude! [{workflow_name}]({run_url}) takes the stage on this {event_type}...\",\"runSuccess\":\"🎤 Bravo! [{workflow_name}]({run_url}) has delivered a stunning performance! Standing ovation! 🌟\",\"runFailure\":\"🎵 Intermission... [{workflow_name}]({run_url}) {status}. The show must go on... eventually!\"}"
GH_AW_WORKFLOW_ID: "cloclo"
GH_AW_WORKFLOW_NAME: "/cloclo"
@@ -1630,7 +1512,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1685,9 +1567,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1704,6 +1586,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: cloclo
steps:
- name: Checkout actions folder
@@ -1716,7 +1599,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/cloclo.md b/.github/workflows/cloclo.md
index 6cba6c17f26..77103bd6abe 100644
--- a/.github/workflows/cloclo.md
+++ b/.github/workflows/cloclo.md
@@ -2,9 +2,7 @@
on:
slash_command:
name: cloclo
- issues:
- types: [labeled]
- names: [cloclo]
+ label_command: cloclo
status-comment: true
permissions:
contents: read
diff --git a/.github/workflows/code-scanning-fixer.lock.yml b/.github/workflows/code-scanning-fixer.lock.yml
index edb85c2e3b4..b7bbfe9f5f0 100644
--- a/.github/workflows/code-scanning-fixer.lock.yml
+++ b/.github/workflows/code-scanning-fixer.lock.yml
@@ -27,7 +27,7 @@
# Imports:
# - shared/activation-app.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"c86a8c8936d098ce2da4bf71678186f60eb9aaed93086ab0692137208bd21398","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"c14677e54f929687c495e807475e5c10c37bda22ea50ca942f575a07dbdecfa8","strict":true}
name: "Code Scanning Fixer"
"on":
@@ -63,7 +63,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -78,14 +78,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -102,9 +102,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "code-scanning-fixer.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -119,22 +119,22 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt_multi.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt_multi.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, add_labels, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -166,6 +166,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -182,9 +183,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -206,10 +207,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -234,11 +235,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -262,10 +263,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: codescanningfixer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -287,16 +289,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -313,7 +319,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/campaigns
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -336,230 +342,49 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
- CUSTOM_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_labels":{"allowed":["agentic-campaign","z_campaign_security-alert-burndown"],"max":3},"create_pull_request":{"expires":48,"max":1,"reviewers":["copilot"],"title_prefix":"[code-scanning-fix] "},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/campaigns","id":"campaigns","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[code-scanning-fix] \". Labels [\"security\" \"automated-fix\" \"agentic-campaign\" \"z_campaign_security-alert-burndown\"] will be automatically added. Reviewers [\"copilot\"] will be assigned.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Add labels to an existing GitHub issue or pull request for categorization and filtering. Labels must already exist in the repository. For creating new issues with labels, use create_issue with the labels property instead. CONSTRAINTS: Only these labels are allowed: [\"agentic-campaign\" \"z_campaign_security-alert-burndown\"].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). If omitted, adds labels to the issue or PR that triggered this workflow. Only works for issue or pull_request event triggers. For schedule, workflow_dispatch, or other triggers, item_number is required — omitting it will silently skip the label operation.",
- "type": "number"
- },
- "labels": {
- "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "add_labels"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_labels": " CONSTRAINTS: Only these labels are allowed: [\"agentic-campaign\" \"z_campaign_security-alert-burndown\"].",
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[code-scanning-fix] \". Labels [\"security\" \"automated-fix\" \"agentic-campaign\" \"z_campaign_security-alert-burndown\"] will be automatically added. Reviewers [\"copilot\"] will be assigned."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_labels": {
"defaultMax": 5,
@@ -675,6 +500,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -699,8 +525,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -711,7 +537,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -719,7 +545,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -737,10 +564,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -748,10 +575,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,code_security,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -759,6 +591,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -776,7 +615,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -785,7 +625,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -814,7 +654,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -852,15 +692,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -869,7 +709,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -886,9 +726,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -897,18 +737,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -999,9 +839,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1024,7 +864,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1052,9 +892,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1100,6 +940,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-code-scanning-fixer"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1115,7 +957,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1139,9 +981,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1152,9 +994,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1179,9 +1021,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1196,9 +1038,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1210,9 +1052,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
pre_activation:
@@ -1233,7 +1075,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1242,10 +1084,19 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
+ - name: Generate GitHub App token for skip-if checks
+ id: pre-activation-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
- name: Check skip-if-match query
id: check_skip_if_match
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1254,10 +1105,11 @@ jobs:
GH_AW_WORKFLOW_NAME: "Code Scanning Fixer"
GH_AW_SKIP_MAX_MATCHES: "1"
with:
+ github-token: ${{ steps.pre-activation-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_skip_if_match.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_skip_if_match.cjs');
await main();
push_repo_memory:
@@ -1269,6 +1121,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
patch_size_exceeded_campaigns: ${{ steps.push_repo_memory_campaigns.outputs.patch_size_exceeded }}
validation_error_campaigns: ${{ steps.push_repo_memory_campaigns.outputs.validation_error }}
@@ -1284,7 +1138,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1327,9 +1181,9 @@ jobs:
FILE_GLOB_FILTER: "security-alert-burndown/**"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1346,6 +1200,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/code-scanning-fixer"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "code-scanning-fixer"
GH_AW_WORKFLOW_NAME: "Code Scanning Fixer"
outputs:
@@ -1368,7 +1223,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1423,9 +1278,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1442,6 +1297,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: codescanningfixer
steps:
- name: Checkout actions folder
@@ -1454,7 +1310,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/code-simplifier.lock.yml b/.github/workflows/code-simplifier.lock.yml
index f75ec3ac119..68a17957b4b 100644
--- a/.github/workflows/code-simplifier.lock.yml
+++ b/.github/workflows/code-simplifier.lock.yml
@@ -28,7 +28,7 @@
# - shared/activation-app.md
# - shared/reporting.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"46fcb0db5d0eb563835594c8aa08fa38761aa25be0bc7d3dae4293d504a5754e","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"b81fd8e811bbc44fdb5badcef171ec839c7b1ef25a6edadce1e74b0a5c9afc8d","strict":true}
name: "Code Simplifier"
"on":
@@ -68,7 +68,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -83,18 +83,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["go"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -112,9 +112,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "code-simplifier.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -129,20 +129,20 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -174,6 +174,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -195,9 +196,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -214,10 +215,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -237,11 +238,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -266,10 +267,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: codesimplifier
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -291,13 +293,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -320,16 +326,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -338,167 +344,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request":{"expires":24,"max":1,"reviewers":["copilot"],"title_prefix":"[code-simplifier] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[code-simplifier] \". Labels [\"refactoring\" \"code-quality\" \"automation\"] will be automatically added. Reviewers [\"copilot\"] will be assigned.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[code-simplifier] \". Labels [\"refactoring\" \"code-quality\" \"automation\"] will be automatically added. Reviewers [\"copilot\"] will be assigned."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -595,6 +464,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -619,8 +489,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -631,7 +501,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -639,7 +509,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -657,10 +528,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -668,10 +539,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -679,6 +555,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -696,7 +579,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -705,7 +589,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,go.dev,golang.org,goproxy.io,host.docker.internal,pkg.go.dev,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,go.dev,golang.org,goproxy.io,host.docker.internal,pkg.go.dev,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -733,7 +617,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -771,15 +655,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -789,7 +673,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -806,9 +690,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -817,18 +701,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -904,9 +788,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -929,7 +813,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -956,9 +840,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1002,6 +886,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-code-simplifier"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1017,7 +903,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1042,9 +928,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1056,9 +942,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1081,9 +967,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1099,9 +985,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1114,9 +1000,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
pre_activation:
@@ -1137,7 +1023,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1146,10 +1032,19 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
+ - name: Generate GitHub App token for skip-if checks
+ id: pre-activation-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
- name: Check skip-if-match query
id: check_skip_if_match
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1158,10 +1053,11 @@ jobs:
GH_AW_WORKFLOW_NAME: "Code Simplifier"
GH_AW_SKIP_MAX_MATCHES: "1"
with:
+ github-token: ${{ steps.pre-activation-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_skip_if_match.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_skip_if_match.cjs');
await main();
safe_outputs:
@@ -1178,6 +1074,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/code-simplifier"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "code-simplifier"
GH_AW_WORKFLOW_ID: "code-simplifier"
GH_AW_WORKFLOW_NAME: "Code Simplifier"
@@ -1201,7 +1098,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1256,9 +1153,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/codex-github-remote-mcp-test.lock.yml b/.github/workflows/codex-github-remote-mcp-test.lock.yml
index 49fe142e6b0..ef98441188c 100644
--- a/.github/workflows/codex-github-remote-mcp-test.lock.yml
+++ b/.github/workflows/codex-github-remote-mcp-test.lock.yml
@@ -57,7 +57,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -72,18 +72,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate CODEX_API_KEY or OPENAI_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
@@ -102,9 +102,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "codex-github-remote-mcp-test.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -118,14 +118,14 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
cat << 'GH_AW_PROMPT_EOF'
The following GitHub context information is available for this workflow:
@@ -156,6 +156,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -170,9 +171,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -188,10 +189,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -210,11 +211,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -232,6 +233,7 @@ jobs:
contents: read
issues: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: codexgithubremotemcptest
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -247,13 +249,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -276,9 +282,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -288,7 +294,7 @@ jobs:
- name: Install Codex
run: npm install -g @openai/codex@latest
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -297,14 +303,15 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15
- name: Start MCP Gateway
id: start-mcp-gateway
env:
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -322,7 +329,7 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="codex"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -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 -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
cat > /tmp/gh-aw/mcp-config/config.toml << GH_AW_MCP_CONFIG_EOF
[history]
@@ -341,7 +348,7 @@ jobs:
GH_AW_MCP_CONFIG_EOF
# Generate JSON config for MCP gateway
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -349,9 +356,14 @@ jobs:
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer $GITHUB_MCP_SERVER_TOKEN",
- "X-MCP-Lockdown": "$([ "$GITHUB_MCP_LOCKDOWN" = "1" ] && echo true || echo false)",
"X-MCP-Readonly": "true",
"X-MCP-Toolsets": "repos,issues"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
}
},
@@ -369,13 +381,14 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Codex
run: |
set -o pipefail
mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.githubcopilot.com,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.githubcopilot.com,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" && codex ${GH_AW_MODEL_DETECTION_CODEX:+-c model="$GH_AW_MODEL_DETECTION_CODEX" }exec -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
@@ -413,15 +426,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'CODEX_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY'
@@ -432,7 +445,7 @@ jobs:
SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Parse agent logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -440,18 +453,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_codex_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_codex_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
diff --git a/.github/workflows/commit-changes-analyzer.lock.yml b/.github/workflows/commit-changes-analyzer.lock.yml
index 068f2484c80..9520a52a8e0 100644
--- a/.github/workflows/commit-changes-analyzer.lock.yml
+++ b/.github/workflows/commit-changes-analyzer.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,18 +81,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -110,9 +110,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "commit-changes-analyzer.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -128,15 +128,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -170,6 +170,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -189,9 +190,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -208,10 +209,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -231,11 +232,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -258,10 +259,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: commitchangesanalyzer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -282,13 +284,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -311,9 +317,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -321,7 +327,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -332,152 +338,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"dev\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"dev\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -564,6 +448,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -588,8 +473,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -600,7 +485,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -608,7 +493,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -626,19 +512,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -646,6 +537,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -663,7 +561,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -739,7 +638,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --max-turns 100 --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools Bash,BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -784,15 +683,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -802,7 +701,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -819,9 +718,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -830,18 +729,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -914,9 +813,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -949,7 +848,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -977,9 +876,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1023,6 +922,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-commit-changes-analyzer"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1038,7 +939,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1062,9 +963,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1075,9 +976,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1098,9 +999,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1115,9 +1016,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1132,6 +1033,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/commit-changes-analyzer"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "commit-changes-analyzer"
GH_AW_WORKFLOW_NAME: "Commit Changes Analyzer"
outputs:
@@ -1152,7 +1054,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1178,9 +1080,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/constraint-solving-potd.lock.yml b/.github/workflows/constraint-solving-potd.lock.yml
index c0d19a89c9d..814bceef853 100644
--- a/.github/workflows/constraint-solving-potd.lock.yml
+++ b/.github/workflows/constraint-solving-potd.lock.yml
@@ -59,7 +59,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -74,18 +74,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -103,9 +103,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "constraint-solving-potd.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -120,16 +120,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -163,6 +163,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -176,9 +177,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -197,10 +198,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -222,11 +223,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -250,10 +251,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: constraintsolvingpotd
outputs:
detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
@@ -274,16 +276,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -304,11 +310,11 @@ jobs:
git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
echo "Git configured with standard GitHub Actions identity"
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -317,152 +323,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":168,"max":1},"max_bot_mentions":1,"mentions":{"enabled":false},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"🧩 Constraint Solving POTD:\". Discussions will be created in category \"announcements\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"🧩 Constraint Solving POTD:\". Discussions will be created in category \"announcements\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -549,6 +433,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -573,8 +458,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -585,7 +470,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -593,7 +478,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -611,10 +497,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -622,10 +508,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -633,6 +524,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -650,7 +548,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -659,7 +558,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -687,7 +586,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -725,15 +624,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -743,7 +642,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -761,9 +660,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -772,18 +671,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -864,9 +763,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -889,7 +788,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -916,9 +815,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -963,6 +862,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-constraint-solving-potd"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -978,7 +879,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1002,9 +903,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1015,9 +916,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1038,9 +939,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1055,9 +956,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1072,6 +973,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/constraint-solving-potd"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "constraint-solving-potd"
GH_AW_WORKFLOW_NAME: "Constraint Solving — Problem of the Day"
outputs:
@@ -1092,7 +994,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1118,9 +1020,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1137,6 +1039,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: constraintsolvingpotd
steps:
- name: Checkout actions folder
@@ -1149,7 +1052,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/contribution-check.lock.yml b/.github/workflows/contribution-check.lock.yml
index 2d8511ad8f8..9967b6ad3fd 100644
--- a/.github/workflows/contribution-check.lock.yml
+++ b/.github/workflows/contribution-check.lock.yml
@@ -22,7 +22,7 @@
# For more information: https://github.github.com/gh-aw/introduction/overview/
#
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"4de9281fdf89dba8197d91de6339b21a8b01ddb1645d17de1f09b3a70fc4cf53","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"23a89d50ad95694dbbfba8c7872a41f03697c87526de949a7725c12497d05d1c","strict":true}
name: "Contribution Check"
"on":
@@ -62,7 +62,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -77,18 +77,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -106,9 +106,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "contribution-check.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -124,15 +124,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, create_issue, add_labels, missing_tool, missing_data, noop
@@ -166,6 +166,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -181,9 +182,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -200,10 +201,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -223,11 +224,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -252,10 +253,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: contributioncheck
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -277,13 +279,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -306,241 +312,40 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":10,"target":"*","target-repo":"${{ vars.TARGET_REPOSITORY }}"},"add_labels":{"allowed":["spam","needs-work","outdated","lgtm"],"max":4,"target":"*","target-repo":"${{ vars.TARGET_REPOSITORY }}"},"create_issue":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[Contribution Check Report]\". Labels [\"contribution-report\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 10 comment(s) can be added. Target: *. Comments will be added in repository \"${{ vars.TARGET_REPOSITORY }}\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Add labels to an existing GitHub issue or pull request for categorization and filtering. Labels must already exist in the repository. For creating new issues with labels, use create_issue with the labels property instead. CONSTRAINTS: Maximum 4 label(s) can be added. Only these labels are allowed: [\"spam\" \"needs-work\" \"outdated\" \"lgtm\"]. Target: *.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). If omitted, adds labels to the issue or PR that triggered this workflow. Only works for issue or pull_request event triggers. For schedule, workflow_dispatch, or other triggers, item_number is required — omitting it will silently skip the label operation.",
- "type": "number"
- },
- "labels": {
- "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "add_labels"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 10 comment(s) can be added. Target: *. Comments will be added in repository \"${{ vars.TARGET_REPOSITORY }}\".",
+ "add_labels": " CONSTRAINTS: Maximum 4 label(s) can be added. Only these labels are allowed: [\"spam\" \"needs-work\" \"outdated\" \"lgtm\"]. Target: *.",
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[Contribution Check Report]\". Labels [\"contribution-report\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -671,6 +476,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -695,8 +501,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -707,7 +513,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -732,10 +538,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -746,6 +552,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "none",
+ "repos": "all"
+ }
}
},
"safeoutputs": {
@@ -753,6 +565,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -770,7 +589,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -779,7 +599,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -807,7 +627,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -845,15 +665,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -863,7 +683,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -880,9 +700,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -891,18 +711,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -977,9 +797,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1002,7 +822,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1029,9 +849,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1076,6 +896,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-contribution-check"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1091,7 +913,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1115,9 +937,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1128,9 +950,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1150,9 +972,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1167,9 +989,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1185,6 +1007,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/contribution-check"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "contribution-check"
GH_AW_WORKFLOW_NAME: "Contribution Check"
outputs:
@@ -1209,7 +1032,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1235,9 +1058,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/contribution-check.md b/.github/workflows/contribution-check.md
index bb821a09563..c26f2c6b529 100644
--- a/.github/workflows/contribution-check.md
+++ b/.github/workflows/contribution-check.md
@@ -15,7 +15,8 @@ env:
tools:
github:
toolsets: [default]
- lockdown: false
+ repos: all
+ min-integrity: none
safe-outputs:
create-issue:
title-prefix: "[Contribution Check Report]"
diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml
index 34d8a1158f8..84f306c7c7e 100644
--- a/.github/workflows/copilot-agent-analysis.lock.yml
+++ b/.github/workflows/copilot-agent-analysis.lock.yml
@@ -67,7 +67,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -82,18 +82,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -111,9 +111,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "copilot-agent-analysis.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -129,17 +129,17 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -173,6 +173,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -199,9 +200,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -226,10 +227,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -257,11 +258,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -287,10 +288,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: copilotagentanalysis
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -311,13 +313,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
- env:
@@ -328,7 +334,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -345,7 +351,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -368,9 +374,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -378,7 +384,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -389,167 +395,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[copilot-agent-analysis] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[copilot-agent-analysis] \". Discussions will be created in category \"audits\"."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -636,6 +505,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -660,8 +530,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -672,7 +542,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -680,7 +550,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -698,19 +569,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -718,6 +594,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -735,7 +618,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -815,7 +699,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash,BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -859,15 +743,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -877,7 +761,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -889,14 +773,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -905,18 +789,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1004,9 +888,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1039,7 +923,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1067,9 +951,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1115,6 +999,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-copilot-agent-analysis"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1130,7 +1016,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1154,9 +1040,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1167,9 +1053,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1194,9 +1080,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1211,9 +1097,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1225,6 +1111,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1240,7 +1128,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1283,9 +1171,9 @@ jobs:
FILE_GLOB_FILTER: "memory/copilot-agent-analysis/*.json memory/copilot-agent-analysis/*.jsonl memory/copilot-agent-analysis/*.csv memory/copilot-agent-analysis/*.md"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1300,6 +1188,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/copilot-agent-analysis"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "copilot-agent-analysis"
GH_AW_WORKFLOW_NAME: "Copilot Agent PR Analysis"
outputs:
@@ -1320,7 +1209,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1339,16 +1228,16 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[copilot-agent-analysis] \"},\"missing_data\":{},\"missing_tool\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1365,6 +1254,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: copilotagentanalysis
steps:
- name: Checkout actions folder
@@ -1377,7 +1267,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/copilot-cli-deep-research.lock.yml b/.github/workflows/copilot-cli-deep-research.lock.yml
index 7c3f6ed23ae..b6cfc977df3 100644
--- a/.github/workflows/copilot-cli-deep-research.lock.yml
+++ b/.github/workflows/copilot-cli-deep-research.lock.yml
@@ -63,7 +63,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -78,14 +78,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -102,9 +102,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "copilot-cli-deep-research.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -120,16 +120,16 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -163,6 +163,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -182,9 +183,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -206,10 +207,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -234,11 +235,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -266,10 +267,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: copilotclideepresearch
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -291,13 +293,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Repo memory git-based storage configuration from frontmatter processed below
- name: Clone repo-memory branch (default)
env:
@@ -307,7 +313,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -330,16 +336,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -348,167 +354,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":204800,"max_patch_size":10240}]}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[copilot-cli-research] \". Discussions will be created in category \"research\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[copilot-cli-research] \". Discussions will be created in category \"research\"."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -595,6 +464,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -619,8 +489,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -631,7 +501,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -639,7 +509,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -657,10 +528,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -668,10 +539,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,actions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -679,6 +555,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -696,7 +579,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -727,7 +611,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat pkg/workflow/copilot*.go)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find .github -name '\''\'\'''\''*.md'\''\'\'''\'')'\'' --allow-tool '\''shell(find .github -type f -exec cat {} +)'\'' --allow-tool '\''shell(find pkg -name '\''\'\'''\''copilot*.go'\''\'\'''\'')'\'' --allow-tool '\''shell(git diff)'\'' --allow-tool '\''shell(git log --oneline)'\'' --allow-tool '\''shell(grep -r *)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -756,7 +640,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -794,15 +678,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -811,7 +695,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -823,14 +707,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -839,18 +723,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -934,9 +818,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -959,7 +843,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -987,9 +871,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1034,6 +918,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-copilot-cli-deep-research"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1049,7 +935,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1073,9 +959,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1086,9 +972,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1113,9 +999,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1130,9 +1016,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1144,6 +1030,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1159,7 +1047,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1202,9 +1090,9 @@ jobs:
FILE_GLOB_FILTER: "memory/copilot-cli-research/*.json memory/copilot-cli-research/*.md"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1219,6 +1107,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/copilot-cli-deep-research"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "copilot-cli-deep-research"
GH_AW_WORKFLOW_NAME: "Copilot CLI Deep Research Agent"
outputs:
@@ -1239,7 +1128,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1258,16 +1147,16 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"research\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[copilot-cli-research] \"},\"missing_data\":{},\"missing_tool\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/copilot-pr-merged-report.lock.yml b/.github/workflows/copilot-pr-merged-report.lock.yml
index 2b5e4b820fc..dd86a7ee11e 100644
--- a/.github/workflows/copilot-pr-merged-report.lock.yml
+++ b/.github/workflows/copilot-pr-merged-report.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,14 +81,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github","api.github.com"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "false"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -105,9 +105,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "copilot-pr-merged-report.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -122,16 +122,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -165,6 +165,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -195,9 +196,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -216,10 +217,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -241,11 +242,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -272,10 +273,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: copilotprmergedreport
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -297,13 +299,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
- env:
@@ -314,7 +320,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -344,16 +350,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -362,152 +368,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[copilot-pr-merged-report] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[copilot-pr-merged-report] \". Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -594,6 +478,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -618,8 +503,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -630,16 +515,16 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Setup MCP Scripts Config
run: |
- mkdir -p /opt/gh-aw/mcp-scripts/logs
- cat > /opt/gh-aw/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
+ mkdir -p ${GH_AW_HOME}/mcp-scripts/logs
+ cat > ${GH_AW_HOME}/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
{
"serverName": "mcpscripts",
"version": "1.0.0",
- "logDir": "/opt/gh-aw/mcp-scripts/logs",
+ "logDir": "${GH_AW_HOME}/mcp-scripts/logs",
"tools": [
{
"name": "gh",
@@ -666,7 +551,7 @@ jobs:
]
}
GH_AW_MCP_SCRIPTS_TOOLS_EOF
- cat > /opt/gh-aw/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
const path = require("path");
const { startHttpServer } = require("./mcp_scripts_mcp_server_http.cjs");
const configPath = path.join(__dirname, "tools.json");
@@ -675,17 +560,17 @@ jobs:
startHttpServer(configPath, {
port: port,
stateless: true,
- logDir: "/opt/gh-aw/mcp-scripts/logs"
+ logDir: process.env.GH_AW_HOME + "/mcp-scripts/logs"
}).catch(error => {
console.error("Failed to start mcp-scripts HTTP server:", error);
process.exit(1);
});
GH_AW_MCP_SCRIPTS_SERVER_EOF
- chmod +x /opt/gh-aw/mcp-scripts/mcp-server.cjs
+ chmod +x ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs
- name: Setup MCP Scripts Tool Files
run: |
- cat > /opt/gh-aw/mcp-scripts/gh.sh << 'GH_AW_MCP_SCRIPTS_SH_GH_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/gh.sh << 'GH_AW_MCP_SCRIPTS_SH_GH_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: gh
# Execute any gh CLI command. This tool is accessible as 'mcpscripts-gh'. Provide the full command after 'gh' (e.g., args: 'pr list --limit 5'). The tool will run: gh . Use single quotes ' for complex args to avoid shell interpretation issues.
@@ -697,7 +582,7 @@ jobs:
GH_TOKEN="$GH_AW_GH_TOKEN" gh $INPUT_ARGS
GH_AW_MCP_SCRIPTS_SH_GH_EOF
- chmod +x /opt/gh-aw/mcp-scripts/gh.sh
+ chmod +x ${GH_AW_HOME}/mcp-scripts/gh.sh
- name: Generate MCP Scripts Server Config
id: mcp-scripts-config
@@ -731,7 +616,7 @@ jobs:
export GH_AW_MCP_SCRIPTS_PORT
export GH_AW_MCP_SCRIPTS_API_KEY
- bash /opt/gh-aw/actions/start_mcp_scripts_server.sh
+ bash ${GH_AW_HOME}/actions/start_mcp_scripts_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -743,7 +628,8 @@ jobs:
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
GH_DEBUG: 1
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -761,10 +647,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e GH_AW_GH_TOKEN -e GH_DEBUG -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e GH_AW_GH_TOKEN -e GH_DEBUG -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -772,10 +658,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"mcpscripts": {
@@ -783,6 +674,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_MCP_SCRIPTS_PORT",
"headers": {
"Authorization": "\${GH_AW_MCP_SCRIPTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"safeoutputs": {
@@ -790,6 +688,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -807,7 +712,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -816,7 +722,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -847,7 +753,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -885,15 +791,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -902,7 +808,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -914,14 +820,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -930,27 +836,27 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Scripts logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_scripts_logs.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_scripts_logs.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1032,9 +938,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1057,7 +963,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1085,9 +991,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1132,6 +1038,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-copilot-pr-merged-report"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1147,7 +1055,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1171,9 +1079,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1184,9 +1092,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1207,9 +1115,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1224,9 +1132,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1241,6 +1149,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/copilot-pr-merged-report"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "copilot-pr-merged-report"
GH_AW_WORKFLOW_NAME: "Daily Copilot PR Merged Report"
outputs:
@@ -1261,7 +1170,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1280,16 +1189,16 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[copilot-pr-merged-report] \"},\"missing_data\":{},\"missing_tool\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1306,6 +1215,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: copilotprmergedreport
steps:
- name: Checkout actions folder
@@ -1318,7 +1228,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml
index 4a932f8df26..f38ea830845 100644
--- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml
+++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml
@@ -28,10 +28,11 @@
# - shared/copilot-pr-data-fetch.md
# - shared/jqschema.md
# - shared/python-dataviz.md
+# - shared/python-nlp.md
# - shared/reporting.md
# - shared/copilot-pr-analysis-base.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"f2b6098d37f2490aa73983346ec72a1b9b929981244ae63b5af8099ce04a78b8","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"a25428ae2bcd93270dfa698f1b2d9de5fcbefaef683f825a257b7d9d9b5977d8","strict":true}
name: "Copilot PR Conversation NLP Analysis"
"on":
@@ -66,7 +67,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,14 +82,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","node","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -105,9 +106,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "copilot-pr-nlp-analysis.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -123,17 +124,17 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, upload_asset, missing_tool, missing_data, noop
@@ -169,6 +170,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -179,6 +181,9 @@ jobs:
{{#runtime-import .github/workflows/shared/python-dataviz.md}}
GH_AW_PROMPT_EOF
cat << 'GH_AW_PROMPT_EOF'
+ {{#runtime-import .github/workflows/shared/python-nlp.md}}
+ GH_AW_PROMPT_EOF
+ cat << 'GH_AW_PROMPT_EOF'
{{#runtime-import .github/workflows/shared/reporting.md}}
GH_AW_PROMPT_EOF
cat << 'GH_AW_PROMPT_EOF'
@@ -199,9 +204,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -226,10 +231,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -257,11 +262,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -288,10 +293,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: copilotprnlpanalysis
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -313,13 +319,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup Python environment
run: "# Create working directory for Python scripts\nmkdir -p /tmp/gh-aw/python\nmkdir -p /tmp/gh-aw/python/data\nmkdir -p /tmp/gh-aw/python/charts\nmkdir -p /tmp/gh-aw/python/artifacts\n\necho \"Python environment setup complete\"\necho \"Working directory: /tmp/gh-aw/python\"\necho \"Data directory: /tmp/gh-aw/python/data\"\necho \"Charts directory: /tmp/gh-aw/python/charts\"\necho \"Artifacts directory: /tmp/gh-aw/python/artifacts\"\n"
- name: Install Python scientific libraries
@@ -342,6 +352,8 @@ jobs:
/tmp/gh-aw/python/*.py
/tmp/gh-aw/python/data/*
retention-days: 30
+ - name: Setup Python NLP environment
+ run: "mkdir -p /tmp/gh-aw/python/{data,charts,artifacts}\npip install --user --quiet nltk scikit-learn textblob wordcloud\n\n# Download required NLTK corpora\npython3 -c \"\nimport nltk\nfor corpus in ['punkt_tab', 'stopwords', 'vader_lexicon', 'averaged_perceptron_tagger_eng']:\n nltk.download(corpus, quiet=True)\nprint('NLTK corpora ready')\n\"\n\npython3 -c \"import sklearn; print(f'scikit-learn {sklearn.__version__}')\""
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
- env:
@@ -354,12 +366,10 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
name: Fetch PR comments for detailed analysis
run: "# Create comments directory\nmkdir -p /tmp/gh-aw/pr-comments\n\n# Fetch detailed comments for each PR from the pre-fetched data\nPR_COUNT=$(jq 'length' /tmp/gh-aw/pr-data/copilot-prs.json)\necho \"Fetching comments for $PR_COUNT PRs...\"\n\njq -r '.[].number' /tmp/gh-aw/pr-data/copilot-prs.json | while read -r PR_NUM; do\n echo \"Fetching comments for PR #${PR_NUM}\"\n gh pr view \"${PR_NUM}\" \\\n --json comments,reviews,reviewComments \\\n > \"/tmp/gh-aw/pr-comments/pr-${PR_NUM}.json\" 2>/dev/null || echo \"{}\" > \"/tmp/gh-aw/pr-comments/pr-${PR_NUM}.json\"\n sleep 0.5 # Rate limiting\ndone\n\necho \"Comment data saved to /tmp/gh-aw/pr-comments/\"\n"
- - name: Install NLP libraries
- run: "pip install --user --quiet nltk scikit-learn textblob wordcloud\n\n# Download required NLTK data (using punkt_tab for NLTK 3.9+)\npython3 -c \"import nltk; nltk.download('punkt_tab', quiet=True); nltk.download('stopwords', quiet=True); nltk.download('vader_lexicon', quiet=True); nltk.download('averaged_perceptron_tagger_eng', quiet=True)\"\n\n# Verify installations\npython3 -c \"import nltk; print(f'NLTK {nltk.__version__} installed')\"\npython3 -c \"import sklearn; print(f'scikit-learn {sklearn.__version__} installed')\"\npython3 -c \"from importlib.metadata import version; print(f'TextBlob {version(\\\"textblob\\\")} installed')\"\npython3 -c \"import wordcloud; print(f'WordCloud {wordcloud.__version__} installed')\"\n\necho \"All NLP libraries installed successfully\"\n"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -376,7 +386,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -399,16 +409,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -417,192 +427,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[nlp-analysis] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[nlp-analysis] \". Discussions will be created in category \"audits\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -698,6 +547,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -722,8 +572,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -734,7 +584,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -745,7 +595,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -763,10 +614,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -774,10 +625,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -785,6 +641,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -802,7 +665,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -811,7 +675,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,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.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,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.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -843,7 +707,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -881,15 +745,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -898,7 +762,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -915,9 +779,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -926,18 +790,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1036,9 +900,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1061,7 +925,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1089,9 +953,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1138,6 +1002,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-copilot-pr-nlp-analysis"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1153,7 +1019,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1177,9 +1043,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1190,9 +1056,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1217,9 +1083,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1234,9 +1100,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1248,6 +1114,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1263,7 +1131,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1306,9 +1174,9 @@ jobs:
FILE_GLOB_FILTER: "memory/nlp-analysis/*.json memory/nlp-analysis/*.jsonl memory/nlp-analysis/*.csv memory/nlp-analysis/*.md"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1323,6 +1191,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/copilot-pr-nlp-analysis"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "copilot-pr-nlp-analysis"
GH_AW_WORKFLOW_NAME: "Copilot PR Conversation NLP Analysis"
outputs:
@@ -1343,7 +1212,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1369,9 +1238,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1388,6 +1257,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: copilotprnlpanalysis
steps:
- name: Checkout actions folder
@@ -1400,7 +1270,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1445,7 +1315,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1500,8 +1370,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/copilot-pr-nlp-analysis.md b/.github/workflows/copilot-pr-nlp-analysis.md
index 7e9d89a3d18..bf5ccfe30ad 100644
--- a/.github/workflows/copilot-pr-nlp-analysis.md
+++ b/.github/workflows/copilot-pr-nlp-analysis.md
@@ -34,6 +34,7 @@ safe-outputs:
imports:
- shared/copilot-pr-analysis-base.md
- shared/python-dataviz.md
+ - shared/python-nlp.md
- shared/reporting.md
tools:
@@ -66,21 +67,6 @@ steps:
echo "Comment data saved to /tmp/gh-aw/pr-comments/"
- - name: Install NLP libraries
- run: |
- pip install --user --quiet nltk scikit-learn textblob wordcloud
-
- # Download required NLTK data (using punkt_tab for NLTK 3.9+)
- python3 -c "import nltk; nltk.download('punkt_tab', quiet=True); nltk.download('stopwords', quiet=True); nltk.download('vader_lexicon', quiet=True); nltk.download('averaged_perceptron_tagger_eng', quiet=True)"
-
- # Verify installations
- python3 -c "import nltk; print(f'NLTK {nltk.__version__} installed')"
- python3 -c "import sklearn; print(f'scikit-learn {sklearn.__version__} installed')"
- python3 -c "from importlib.metadata import version; print(f'TextBlob {version(\"textblob\")} installed')"
- python3 -c "import wordcloud; print(f'WordCloud {wordcloud.__version__} installed')"
-
- echo "All NLP libraries installed successfully"
-
timeout-minutes: 20
features:
diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml
index 5d9e098c9f8..74fa2b3445b 100644
--- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml
+++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,14 +81,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","node"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -105,9 +105,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "copilot-pr-prompt-analysis.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -123,17 +123,17 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -167,6 +167,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -194,9 +195,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -221,10 +222,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -252,11 +253,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -283,10 +284,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: copilotprpromptanalysis
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -308,13 +310,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
- env:
@@ -325,7 +331,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -342,7 +348,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -365,16 +371,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -383,167 +389,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[prompt-analysis] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[prompt-analysis] \". Discussions will be created in category \"audits\"."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -630,6 +499,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -654,8 +524,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -666,7 +536,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -674,7 +544,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -692,10 +563,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -703,10 +574,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -714,6 +590,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -731,7 +614,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -740,7 +624,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -769,7 +653,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -807,15 +691,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -824,7 +708,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -841,9 +725,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -852,18 +736,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -953,9 +837,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -978,7 +862,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1006,9 +890,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1054,6 +938,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-copilot-pr-prompt-analysis"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1069,7 +955,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1093,9 +979,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1106,9 +992,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1133,9 +1019,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1150,9 +1036,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1164,6 +1050,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1179,7 +1067,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1222,9 +1110,9 @@ jobs:
FILE_GLOB_FILTER: "memory/prompt-analysis/*.json memory/prompt-analysis/*.jsonl memory/prompt-analysis/*.csv memory/prompt-analysis/*.md"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1239,6 +1127,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/copilot-pr-prompt-analysis"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "copilot-pr-prompt-analysis"
GH_AW_WORKFLOW_NAME: "Copilot PR Prompt Pattern Analysis"
outputs:
@@ -1259,7 +1148,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1285,9 +1174,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1304,6 +1193,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: copilotprpromptanalysis
steps:
- name: Checkout actions folder
@@ -1316,7 +1206,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml
index cb30651fcaa..2aab7255bdb 100644
--- a/.github/workflows/copilot-session-insights.lock.yml
+++ b/.github/workflows/copilot-session-insights.lock.yml
@@ -69,7 +69,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -84,18 +84,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -113,9 +113,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "copilot-session-insights.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -132,17 +132,17 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, upload_asset, missing_tool, missing_data, noop
@@ -178,6 +178,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -212,9 +213,9 @@ jobs:
GH_AW_GITHUB_WORKFLOW: ${{ github.workflow }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -240,10 +241,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -272,11 +273,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -302,10 +303,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: copilotsessioninsights
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -326,13 +328,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
- env:
@@ -365,7 +371,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -382,7 +388,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -405,9 +411,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -415,7 +421,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -426,192 +432,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[copilot-session-insights] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[copilot-session-insights] \". Discussions will be created in category \"audits\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -707,6 +552,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -731,8 +577,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -743,7 +589,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -754,7 +600,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -772,19 +619,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -792,6 +644,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -809,7 +668,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -889,7 +749,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash,BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -936,15 +796,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -954,7 +814,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -966,14 +826,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -982,18 +842,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1090,9 +950,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1125,7 +985,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1153,9 +1013,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1202,6 +1062,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-copilot-session-insights"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1217,7 +1079,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1241,9 +1103,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1254,9 +1116,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1281,9 +1143,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1298,9 +1160,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1312,6 +1174,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1327,7 +1191,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1370,9 +1234,9 @@ jobs:
FILE_GLOB_FILTER: "memory/session-insights/*.json memory/session-insights/*.jsonl memory/session-insights/*.csv memory/session-insights/*.md"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1387,6 +1251,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/copilot-session-insights"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "copilot-session-insights"
GH_AW_WORKFLOW_NAME: "Copilot Session Insights"
outputs:
@@ -1407,7 +1272,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1426,16 +1291,16 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[copilot-session-insights] \"},\"missing_data\":{},\"missing_tool\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1452,6 +1317,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: copilotsessioninsights
steps:
- name: Checkout actions folder
@@ -1464,7 +1330,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1509,7 +1375,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1564,8 +1430,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/craft.lock.yml b/.github/workflows/craft.lock.yml
index 396983624e4..a1075bfdb31 100644
--- a/.github/workflows/craft.lock.yml
+++ b/.github/workflows/craft.lock.yml
@@ -54,8 +54,9 @@ jobs:
pull-requests: write
outputs:
body: ${{ steps.sanitized.outputs.body }}
- comment_id: ""
- comment_repo: ""
+ comment_id: ${{ steps.add-comment.outputs.comment-id }}
+ comment_repo: ${{ steps.add-comment.outputs.comment-repo }}
+ comment_url: ${{ steps.add-comment.outputs.comment-url }}
model: ${{ steps.generate_aw_info.outputs.model }}
slash_command: ${{ needs.pre_activation.outputs.matched_command }}
text: ${{ steps.sanitized.outputs.text }}
@@ -71,7 +72,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -86,14 +87,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -113,9 +114,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -123,18 +124,31 @@ jobs:
GH_AW_WORKFLOW_FILE: "craft.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_WORKFLOW_NAME: "Workflow Craft Agent"
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e ⚒️ *Crafted with care by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🛠️ Master Crafter at work! [{workflow_name}]({run_url}) is forging a new workflow on this {event_type}...\",\"runSuccess\":\"⚒️ Masterpiece complete! [{workflow_name}]({run_url}) has crafted your workflow. May it serve you well! 🎖️\",\"runFailure\":\"🛠️ Forge cooling down! [{workflow_name}]({run_url}) {status}. The anvil awaits another attempt...\"}"
+ with:
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_workflow_run_comment.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -151,20 +165,20 @@ jobs:
GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }}
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, push_to_pull_request_branch, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_push_to_pr_branch.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_push_to_pr_branch.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -196,8 +210,9 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
- cat "/opt/gh-aw/prompts/pr_context_prompt.md"
+ cat "${GH_AW_HOME}/prompts/pr_context_prompt.md"
fi
cat << 'GH_AW_PROMPT_EOF'
@@ -215,9 +230,9 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -237,10 +252,10 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -263,11 +278,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -291,10 +306,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: craft
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -316,13 +332,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Install gh-aw extension
run: |-
gh extension remove gh-aw || true
@@ -350,16 +370,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -368,191 +388,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Push committed changes to a pull request's branch. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. Changes must be committed locally before calling this tool.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "branch": {
- "description": "Branch name to push changes from. If omitted, uses the current working branch. Only specify if you need to push from a different branch.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Commit message describing the changes. Follow repository commit message conventions (e.g., conventional commits).",
- "type": "string"
- },
- "pull_request_number": {
- "description": "Pull request number to push changes to. This is the numeric ID from the GitHub URL (e.g., 654 in github.com/owner/repo/pull/654). Required when the workflow target is '*' (any PR).",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "push_to_pull_request_branch"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -651,6 +510,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -675,8 +535,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -687,7 +547,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -695,7 +555,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -713,10 +574,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -724,10 +585,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -735,6 +601,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -752,7 +625,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -761,7 +635,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -790,7 +664,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -828,15 +702,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -845,7 +719,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -863,9 +737,9 @@ jobs:
GH_AW_COMMAND: craft
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -874,18 +748,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -961,9 +835,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -986,7 +860,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1014,9 +888,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1061,6 +935,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-craft"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1076,7 +952,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1100,9 +976,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1113,9 +989,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1137,9 +1013,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1154,9 +1030,28 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
+ await main();
+ - name: Update reaction comment with completion status
+ id: conclusion
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ env.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: "Workflow Craft Agent"
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.agent.outputs.detection_conclusion }}
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e ⚒️ *Crafted with care by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🛠️ Master Crafter at work! [{workflow_name}]({run_url}) is forging a new workflow on this {event_type}...\",\"runSuccess\":\"⚒️ Masterpiece complete! [{workflow_name}]({run_url}) has crafted your workflow. May it serve you well! 🎖️\",\"runFailure\":\"🛠️ Forge cooling down! [{workflow_name}]({run_url}) {status}. The anvil awaits another attempt...\"}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/notify_comment_error.cjs');
await main();
pre_activation:
@@ -1179,7 +1074,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for command workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1188,9 +1083,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check command position
id: check_command_position
@@ -1199,9 +1094,9 @@ jobs:
GH_AW_COMMANDS: "[\"craft\"]"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_command_position.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_command_position.cjs');
await main();
safe_outputs:
@@ -1219,6 +1114,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/craft"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e ⚒️ *Crafted with care by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🛠️ Master Crafter at work! [{workflow_name}]({run_url}) is forging a new workflow on this {event_type}...\",\"runSuccess\":\"⚒️ Masterpiece complete! [{workflow_name}]({run_url}) has crafted your workflow. May it serve you well! 🎖️\",\"runFailure\":\"🛠️ Forge cooling down! [{workflow_name}]({run_url}) {status}. The anvil awaits another attempt...\"}"
GH_AW_WORKFLOW_ID: "craft"
GH_AW_WORKFLOW_NAME: "Workflow Craft Agent"
@@ -1244,7 +1140,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1299,9 +1195,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-architecture-diagram.lock.yml b/.github/workflows/daily-architecture-diagram.lock.yml
index 2d75ca37d4d..20db506dc51 100644
--- a/.github/workflows/daily-architecture-diagram.lock.yml
+++ b/.github/workflows/daily-architecture-diagram.lock.yml
@@ -63,7 +63,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -78,14 +78,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -102,9 +102,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-architecture-diagram.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -119,21 +119,21 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -165,6 +165,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -181,9 +182,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -202,10 +203,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -227,11 +228,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -257,10 +258,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyarchitecturediagram
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -282,16 +284,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -321,16 +327,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -339,216 +345,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":168,"max":1},"create_pull_request":{"expires":168,"max":1,"title_prefix":"[architecture] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"🏗️ Architecture Diagram:\". Labels [\"architecture\" \"diagram\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[architecture] \". Labels [\"architecture\" \"diagram\" \"documentation\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"🏗️ Architecture Diagram:\". Labels [\"architecture\" \"diagram\"] will be automatically added.",
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[architecture] \". Labels [\"architecture\" \"diagram\" \"documentation\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -678,6 +499,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -702,8 +524,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -714,7 +536,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -722,7 +544,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -740,10 +563,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -751,10 +574,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -762,6 +590,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -779,7 +614,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -788,7 +624,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -817,7 +653,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -855,15 +691,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -872,7 +708,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -889,9 +725,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -900,18 +736,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -993,9 +829,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1018,7 +854,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1046,9 +882,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1093,6 +929,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-architecture-diagram"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1108,7 +946,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1132,9 +970,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1145,9 +983,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1168,9 +1006,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1185,9 +1023,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1199,9 +1037,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
safe_outputs:
@@ -1218,6 +1056,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-architecture-diagram"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "daily-architecture-diagram"
GH_AW_WORKFLOW_NAME: "Architecture Diagram Generator"
outputs:
@@ -1242,7 +1081,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1297,9 +1136,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1316,6 +1155,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: dailyarchitecturediagram
steps:
- name: Checkout actions folder
@@ -1328,7 +1168,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/daily-assign-issue-to-user.lock.yml b/.github/workflows/daily-assign-issue-to-user.lock.yml
index 579f919fd46..37330fe4cf8 100644
--- a/.github/workflows/daily-assign-issue-to-user.lock.yml
+++ b/.github/workflows/daily-assign-issue-to-user.lock.yml
@@ -58,7 +58,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -73,14 +73,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -97,9 +97,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-assign-issue-to-user.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -114,15 +114,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, assign_to_user, missing_tool, missing_data, noop
@@ -156,6 +156,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -169,9 +170,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -187,10 +188,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -209,11 +210,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -239,10 +240,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyassignissuetouser
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -264,13 +266,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -293,16 +299,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -311,194 +317,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":1,"target":"*"},"assign_to_user":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added. Target: *.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Assign one or more GitHub users to an issue. Use this to delegate work to specific team members. Users must have access to the repository.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "assignee": {
- "description": "Single GitHub username to assign. Use 'assignees' array for multiple users.",
- "type": "string"
- },
- "assignees": {
- "description": "GitHub usernames to assign to the issue (e.g., ['octocat', 'mona']). Users must have access to the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "issue_number": {
- "description": "Issue number to assign users to. This is the numeric ID from the GitHub URL (e.g., 543 in github.com/owner/repo/issues/543). If omitted, assigns to the issue that triggered this workflow.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "issue_number"
- ],
- "type": "object"
- },
- "name": "assign_to_user"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Target: *."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -599,6 +441,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -623,8 +466,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -635,7 +478,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -643,7 +486,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -661,10 +505,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -672,10 +516,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "issues,pull_requests,repos"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -683,6 +532,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -700,7 +556,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -709,7 +566,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -738,7 +595,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -776,15 +633,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -793,7 +650,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -810,9 +667,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -821,18 +678,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -907,9 +764,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -932,7 +789,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -960,9 +817,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1007,6 +864,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-assign-issue-to-user"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1022,7 +881,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1046,9 +905,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1059,9 +918,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1080,9 +939,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1097,9 +956,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1115,6 +974,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-assign-issue-to-user"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "daily-assign-issue-to-user"
GH_AW_WORKFLOW_NAME: "Auto-Assign Issue"
outputs:
@@ -1138,7 +998,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1164,9 +1024,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-choice-test.lock.yml b/.github/workflows/daily-choice-test.lock.yml
index 3058be842dc..d99c58e7beb 100644
--- a/.github/workflows/daily-choice-test.lock.yml
+++ b/.github/workflows/daily-choice-test.lock.yml
@@ -59,7 +59,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -74,18 +74,18 @@ jobs:
GH_AW_INFO_STAGED: "true"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -103,9 +103,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-choice-test.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -120,15 +120,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: missing_tool, missing_data, noop
@@ -162,6 +162,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -175,9 +176,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -193,10 +194,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -215,11 +216,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -244,10 +245,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailychoicetest
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -268,13 +270,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -297,9 +303,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -307,7 +313,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -318,149 +324,60 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"missing_data":{},"missing_tool":{},"noop":{"max":1},"test_environment":{"description":"A test job with choice input","inputs":{"environment":{"default":null,"description":"Target environment","options":["staging","production"],"required":true,"type":"choice"},"test_type":{"default":null,"description":"Type of test to run","options":["smoke","integration","e2e"],"required":true,"type":"choice"}},"output":"Environment test completed successfully"}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- },
- {
- "description": "A test job with choice input",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "environment": {
- "description": "Target environment",
- "enum": [
- "staging",
- "production"
- ],
- "type": "string"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {},
+ "repo_params": {},
+ "dynamic_tools": [
+ {
+ "description": "A test job with choice input",
+ "inputSchema": {
+ "additionalProperties": false,
+ "properties": {
+ "environment": {
+ "description": "Target environment",
+ "enum": [
+ "staging",
+ "production"
+ ],
+ "type": "string"
+ },
+ "test_type": {
+ "description": "Type of test to run",
+ "enum": [
+ "smoke",
+ "integration",
+ "e2e"
+ ],
+ "type": "string"
+ }
},
- "test_type": {
- "description": "Type of test to run",
- "enum": [
- "smoke",
- "integration",
- "e2e"
- ],
- "type": "string"
- }
+ "required": [
+ "environment",
+ "test_type"
+ ],
+ "type": "object"
},
- "required": [
- "environment",
- "test_type"
- ],
- "type": "object"
- },
- "name": "test_environment"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "name": "test_environment"
+ }
+ ]
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"missing_data": {
"defaultMax": 20,
@@ -521,6 +438,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -545,8 +463,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -557,7 +475,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -565,7 +483,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -583,19 +502,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -603,6 +527,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -620,7 +551,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -696,7 +628,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools Bash,BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -741,15 +673,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -759,7 +691,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -776,9 +708,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -787,18 +719,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -871,9 +803,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -906,7 +838,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -934,9 +866,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -977,6 +909,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-choice-test"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -992,7 +926,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1017,9 +951,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1031,9 +965,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1053,9 +987,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1071,9 +1005,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1084,6 +1018,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-choice-test"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUTS_STAGED: "true"
GH_AW_TRACKER_ID: "daily-choice-test"
GH_AW_WORKFLOW_ID: "daily-choice-test"
@@ -1106,7 +1041,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1133,9 +1068,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
test_environment:
@@ -1150,11 +1085,11 @@ jobs:
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: agent
- path: /opt/gh-aw/safe-jobs/
+ path: ${{ env.GH_AW_HOME }}/safe-jobs/
- name: Setup Safe Job Environment Variables
run: |
- find "/opt/gh-aw/safe-jobs/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/opt/gh-aw/safe-jobs/agent_output.json" >> "$GITHUB_ENV"
+ find "${{ env.GH_AW_HOME }}/safe-jobs/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=${{ env.GH_AW_HOME }}/safe-jobs/agent_output.json" >> "$GITHUB_ENV"
- name: Display test configuration
run: |-
if [ -f "$GH_AW_AGENT_OUTPUT" ]; then
diff --git a/.github/workflows/daily-cli-performance.lock.yml b/.github/workflows/daily-cli-performance.lock.yml
index 2d06b827489..aeddce5135f 100644
--- a/.github/workflows/daily-cli-performance.lock.yml
+++ b/.github/workflows/daily-cli-performance.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,14 +79,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","go"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -103,9 +103,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-cli-performance.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -122,16 +122,16 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, create_issue, missing_tool, missing_data, noop
@@ -165,6 +165,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -187,9 +188,9 @@ jobs:
GH_AW_GITHUB_SERVER_URL: ${{ github.server_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -212,10 +213,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -241,11 +242,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -271,10 +272,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailycliperformance
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -296,13 +298,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Repo memory git-based storage configuration from frontmatter processed below
- name: Clone repo-memory branch (default)
env:
@@ -312,7 +318,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -335,16 +341,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -353,219 +359,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":5},"create_issue":{"expires":48,"group":true,"max":3},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":512000,"max_patch_size":10240}]}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 3 issue(s) can be created. Title will be prefixed with \"[performance] \". Labels [\"performance\" \"automation\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 5 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 5 comment(s) can be added.",
+ "create_issue": " CONSTRAINTS: Maximum 3 issue(s) can be created. Title will be prefixed with \"[performance] \". Labels [\"performance\" \"automation\" \"cookie\"] will be automatically added."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -677,6 +495,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -701,8 +520,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -713,16 +532,16 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Setup MCP Scripts Config
run: |
- mkdir -p /opt/gh-aw/mcp-scripts/logs
- cat > /opt/gh-aw/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
+ mkdir -p ${GH_AW_HOME}/mcp-scripts/logs
+ cat > ${GH_AW_HOME}/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
{
"serverName": "mcpscripts",
"version": "1.0.0",
- "logDir": "/opt/gh-aw/mcp-scripts/logs",
+ "logDir": "${GH_AW_HOME}/mcp-scripts/logs",
"tools": [
{
"name": "go",
@@ -763,7 +582,7 @@ jobs:
]
}
GH_AW_MCP_SCRIPTS_TOOLS_EOF
- cat > /opt/gh-aw/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
const path = require("path");
const { startHttpServer } = require("./mcp_scripts_mcp_server_http.cjs");
const configPath = path.join(__dirname, "tools.json");
@@ -772,17 +591,17 @@ jobs:
startHttpServer(configPath, {
port: port,
stateless: true,
- logDir: "/opt/gh-aw/mcp-scripts/logs"
+ logDir: process.env.GH_AW_HOME + "/mcp-scripts/logs"
}).catch(error => {
console.error("Failed to start mcp-scripts HTTP server:", error);
process.exit(1);
});
GH_AW_MCP_SCRIPTS_SERVER_EOF
- chmod +x /opt/gh-aw/mcp-scripts/mcp-server.cjs
+ chmod +x ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs
- name: Setup MCP Scripts Tool Files
run: |
- cat > /opt/gh-aw/mcp-scripts/go.sh << 'GH_AW_MCP_SCRIPTS_SH_GO_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/go.sh << 'GH_AW_MCP_SCRIPTS_SH_GO_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: go
# Execute any Go command. This tool is accessible as 'mcpscripts-go'. Provide the full command after 'go' (e.g., args: 'test ./...'). The tool will run: go . Use single quotes ' for complex args to avoid shell interpretation issues.
@@ -794,8 +613,8 @@ jobs:
GH_AW_MCP_SCRIPTS_SH_GO_EOF
- chmod +x /opt/gh-aw/mcp-scripts/go.sh
- cat > /opt/gh-aw/mcp-scripts/make.sh << 'GH_AW_MCP_SCRIPTS_SH_MAKE_EOF'
+ chmod +x ${GH_AW_HOME}/mcp-scripts/go.sh
+ cat > ${GH_AW_HOME}/mcp-scripts/make.sh << 'GH_AW_MCP_SCRIPTS_SH_MAKE_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: make
# Execute any Make target. This tool is accessible as 'mcpscripts-make'. Provide the target name(s) (e.g., args: 'build'). The tool will run: make . Use single quotes ' for complex args to avoid shell interpretation issues.
@@ -806,7 +625,7 @@ jobs:
make $INPUT_ARGS
GH_AW_MCP_SCRIPTS_SH_MAKE_EOF
- chmod +x /opt/gh-aw/mcp-scripts/make.sh
+ chmod +x ${GH_AW_HOME}/mcp-scripts/make.sh
- name: Generate MCP Scripts Server Config
id: mcp-scripts-config
@@ -838,7 +657,7 @@ jobs:
export GH_AW_MCP_SCRIPTS_PORT
export GH_AW_MCP_SCRIPTS_API_KEY
- bash /opt/gh-aw/actions/start_mcp_scripts_server.sh
+ bash ${GH_AW_HOME}/actions/start_mcp_scripts_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -848,7 +667,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -866,10 +686,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -877,10 +697,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"mcpscripts": {
@@ -888,6 +713,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_MCP_SCRIPTS_PORT",
"headers": {
"Authorization": "\${GH_AW_MCP_SCRIPTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"safeoutputs": {
@@ -895,6 +727,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -912,7 +751,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -921,7 +761,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -950,7 +790,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -988,15 +828,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -1005,7 +845,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1022,9 +862,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1033,27 +873,27 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Scripts logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_scripts_logs.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_scripts_logs.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1138,9 +978,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1163,7 +1003,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1191,9 +1031,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1239,6 +1079,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-cli-performance"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1254,7 +1096,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1279,9 +1121,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1293,9 +1135,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1319,9 +1161,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1337,9 +1179,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1351,6 +1193,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1366,7 +1210,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1409,9 +1253,9 @@ jobs:
FILE_GLOB_FILTER: "memory/cli-performance/*.json memory/cli-performance/*.jsonl memory/cli-performance/*.txt"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1427,6 +1271,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-cli-performance"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-cli-performance"
GH_AW_WORKFLOW_ID: "daily-cli-performance"
GH_AW_WORKFLOW_NAME: "Daily CLI Performance Agent"
@@ -1452,7 +1297,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1478,9 +1323,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-cli-tools-tester.lock.yml b/.github/workflows/daily-cli-tools-tester.lock.yml
index 71c0be5ccd5..ebe2187262b 100644
--- a/.github/workflows/daily-cli-tools-tester.lock.yml
+++ b/.github/workflows/daily-cli-tools-tester.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-cli-tools-tester.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,15 +125,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -167,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -185,9 +187,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -203,10 +205,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -225,11 +227,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -255,10 +257,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyclitoolstester
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -280,7 +283,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -316,7 +319,11 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -339,16 +346,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -357,10 +364,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -375,173 +382,36 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":168,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[cli-tools-test] \". Labels [\"testing\" \"automation\" \"cli-tools\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[cli-tools-test] \". Labels [\"testing\" \"automation\" \"cli-tools\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -635,6 +505,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -659,8 +530,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -671,7 +542,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -679,7 +550,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -698,10 +570,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -714,6 +586,13 @@ jobs:
"GITHUB_TOKEN": "\${GITHUB_TOKEN}",
"GITHUB_ACTOR": "\${GITHUB_ACTOR}",
"GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
@@ -721,10 +600,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -732,6 +616,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -749,7 +640,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -758,7 +650,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -786,7 +678,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -824,15 +716,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -842,7 +734,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -859,9 +751,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -870,18 +762,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -956,9 +848,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -981,7 +873,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1008,9 +900,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1053,6 +945,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-cli-tools-tester"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1068,7 +962,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1092,9 +986,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1105,9 +999,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1127,9 +1021,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1144,9 +1038,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1160,6 +1054,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-cli-tools-tester"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "daily-cli-tools-tester"
GH_AW_WORKFLOW_NAME: "Daily CLI Tools Exploratory Tester"
outputs:
@@ -1182,7 +1077,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1208,9 +1103,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-code-metrics.lock.yml b/.github/workflows/daily-code-metrics.lock.yml
index ae04ed5f80e..480499ed9b8 100644
--- a/.github/workflows/daily-code-metrics.lock.yml
+++ b/.github/workflows/daily-code-metrics.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,18 +81,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -110,9 +110,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-code-metrics.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -128,17 +128,17 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, upload_asset, missing_tool, missing_data, noop
@@ -174,6 +174,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -196,9 +197,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -223,10 +224,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -254,11 +255,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -283,10 +284,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailycodemetrics
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -307,13 +309,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup Python environment
run: "# Create working directory for Python scripts\nmkdir -p /tmp/gh-aw/python\nmkdir -p /tmp/gh-aw/python/data\nmkdir -p /tmp/gh-aw/python/charts\nmkdir -p /tmp/gh-aw/python/artifacts\n\necho \"Python environment setup complete\"\necho \"Working directory: /tmp/gh-aw/python\"\necho \"Data directory: /tmp/gh-aw/python/data\"\necho \"Charts directory: /tmp/gh-aw/python/charts\"\necho \"Artifacts directory: /tmp/gh-aw/python/artifacts\"\n"
- name: Install Python scientific libraries
@@ -339,7 +345,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -356,7 +362,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -379,9 +385,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -389,7 +395,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -400,192 +406,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":51200}]},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -681,6 +526,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -705,8 +551,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -717,7 +563,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -728,7 +574,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -746,19 +593,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -766,6 +618,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -783,7 +642,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -863,7 +723,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash,BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -910,15 +770,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -928,7 +788,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -945,9 +805,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -956,18 +816,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1064,9 +924,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1099,7 +959,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1127,9 +987,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1176,6 +1036,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-code-metrics"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1191,7 +1053,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1216,9 +1078,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1230,9 +1092,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1258,9 +1120,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1276,9 +1138,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1290,6 +1152,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1305,7 +1169,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1348,9 +1212,9 @@ jobs:
FILE_GLOB_FILTER: "*.json *.jsonl *.csv *.md"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1365,6 +1229,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-code-metrics"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-code-metrics"
GH_AW_WORKFLOW_ID: "daily-code-metrics"
GH_AW_WORKFLOW_NAME: "Daily Code Metrics and Trend Tracking Agent"
@@ -1386,7 +1251,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1412,9 +1277,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1431,6 +1296,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: dailycodemetrics
steps:
- name: Checkout actions folder
@@ -1443,7 +1309,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1488,7 +1354,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1544,8 +1410,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/daily-compiler-quality.lock.yml b/.github/workflows/daily-compiler-quality.lock.yml
index 0ea68e1933f..f51ab5bd584 100644
--- a/.github/workflows/daily-compiler-quality.lock.yml
+++ b/.github/workflows/daily-compiler-quality.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,14 +79,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -103,9 +103,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-compiler-quality.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -120,16 +120,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -163,6 +163,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -184,9 +185,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -205,10 +206,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -230,11 +231,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -260,10 +261,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailycompilerquality
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -285,16 +287,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -324,16 +330,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -342,152 +348,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -574,6 +458,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -598,8 +483,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -610,7 +495,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -618,7 +503,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -636,10 +522,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -647,10 +533,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -658,6 +549,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -666,7 +564,14 @@ jobs:
"args": ["--network", "host"],
"entrypoint": "serena",
"entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -683,7 +588,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -713,7 +619,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool serena --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find pkg/workflow -name '\''\'\'''\''compiler*.go'\''\'\'''\'' ! -name '\''\'\'''\''*_test.go'\''\'\'''\'' -type f)'\'' --allow-tool '\''shell(git diff HEAD~7 -- pkg/workflow/compiler*.go)'\'' --allow-tool '\''shell(git log --since='\''\'\'''\''7 days ago'\''\'\'''\'' --format='\''\'\'''\''%h %s'\''\'\'''\'' -- pkg/workflow/compiler*.go)'\'' --allow-tool '\''shell(git show HEAD:pkg/workflow/compiler*.go)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc -l pkg/workflow/compiler*.go)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -742,7 +648,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -780,15 +686,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -797,7 +703,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -814,9 +720,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -825,18 +731,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -917,9 +823,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -942,7 +848,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -970,9 +876,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1017,6 +923,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-compiler-quality"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1032,7 +940,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1057,9 +965,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1071,9 +979,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1095,9 +1003,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1113,9 +1021,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1130,6 +1038,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-compiler-quality"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-compiler-quality"
GH_AW_WORKFLOW_ID: "daily-compiler-quality"
GH_AW_WORKFLOW_NAME: "Daily Compiler Quality Check"
@@ -1151,7 +1060,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1177,9 +1086,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1196,6 +1105,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: dailycompilerquality
steps:
- name: Checkout actions folder
@@ -1208,7 +1118,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/daily-copilot-token-report.lock.yml b/.github/workflows/daily-copilot-token-report.lock.yml
index 06a5928d87b..fe3989081c6 100644
--- a/.github/workflows/daily-copilot-token-report.lock.yml
+++ b/.github/workflows/daily-copilot-token-report.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,14 +79,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -103,9 +103,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-copilot-token-report.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -121,17 +121,17 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, upload_asset, missing_tool, missing_data, noop
@@ -167,6 +167,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -187,9 +188,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -214,10 +215,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -245,11 +246,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -276,10 +277,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailycopilottokenreport
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -301,9 +303,13 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Go
@@ -367,7 +373,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -384,7 +390,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -407,16 +413,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -425,192 +431,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -706,6 +551,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -730,8 +576,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -742,7 +588,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -753,7 +599,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -771,10 +618,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -782,10 +629,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -793,6 +645,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -810,7 +669,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -819,7 +679,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,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,files.pythonhosted.org,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,static.crates.io,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,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,files.pythonhosted.org,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,static.crates.io,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -851,7 +711,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -889,15 +749,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -906,7 +766,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -923,9 +783,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -934,18 +794,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1044,9 +904,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1069,7 +929,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1097,9 +957,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1146,6 +1006,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-copilot-token-report"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1161,7 +1023,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1186,9 +1048,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1200,9 +1062,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1228,9 +1090,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1246,9 +1108,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1260,6 +1122,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1275,7 +1139,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1318,9 +1182,9 @@ jobs:
FILE_GLOB_FILTER: "memory/token-metrics/*.json memory/token-metrics/*.jsonl memory/token-metrics/*.csv memory/token-metrics/*.md"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1335,6 +1199,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-copilot-token-report"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-copilot-token-report"
GH_AW_WORKFLOW_ID: "daily-copilot-token-report"
GH_AW_WORKFLOW_NAME: "Daily Copilot Token Consumption Report"
@@ -1356,7 +1221,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1382,9 +1247,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1401,6 +1266,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: dailycopilottokenreport
steps:
- name: Checkout actions folder
@@ -1413,7 +1279,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1458,7 +1324,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1514,8 +1380,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/daily-doc-healer.lock.yml b/.github/workflows/daily-doc-healer.lock.yml
index 433cd99bc2b..806e9e8ddff 100644
--- a/.github/workflows/daily-doc-healer.lock.yml
+++ b/.github/workflows/daily-doc-healer.lock.yml
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,18 +80,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -109,9 +109,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-doc-healer.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -126,21 +126,21 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -172,6 +172,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -192,9 +193,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -213,10 +214,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -238,11 +239,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -267,10 +268,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailydochealer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -291,7 +293,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -302,7 +304,11 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Install QMD
run: npm install -g @tobilu/qmd
- name: Restore QMD index cache
@@ -317,7 +323,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -347,9 +353,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -357,7 +363,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -368,216 +374,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":72,"max":1},"create_pull_request":{"expires":72,"max":1,"title_prefix":"[docs] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[doc-healer] \". Labels [\"documentation\" \"automation\"] will be automatically added. Assignees [\"copilot\"] will be automatically assigned.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[docs] \". Labels [\"documentation\" \"automation\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[doc-healer] \". Labels [\"documentation\" \"automation\"] will be automatically added. Assignees [\"copilot\"] will be automatically assigned.",
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[docs] \". Labels [\"documentation\" \"automation\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -707,6 +528,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -731,8 +553,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -743,16 +565,16 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Setup MCP Scripts Config
run: |
- mkdir -p /opt/gh-aw/mcp-scripts/logs
- cat > /opt/gh-aw/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
+ mkdir -p ${GH_AW_HOME}/mcp-scripts/logs
+ cat > ${GH_AW_HOME}/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
{
"serverName": "mcpscripts",
"version": "1.0.0",
- "logDir": "/opt/gh-aw/mcp-scripts/logs",
+ "logDir": "${GH_AW_HOME}/mcp-scripts/logs",
"tools": [
{
"name": "qmd-query",
@@ -780,7 +602,7 @@ jobs:
]
}
GH_AW_MCP_SCRIPTS_TOOLS_EOF
- cat > /opt/gh-aw/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
const path = require("path");
const { startHttpServer } = require("./mcp_scripts_mcp_server_http.cjs");
const configPath = path.join(__dirname, "tools.json");
@@ -789,17 +611,17 @@ jobs:
startHttpServer(configPath, {
port: port,
stateless: true,
- logDir: "/opt/gh-aw/mcp-scripts/logs"
+ logDir: process.env.GH_AW_HOME + "/mcp-scripts/logs"
}).catch(error => {
console.error("Failed to start mcp-scripts HTTP server:", error);
process.exit(1);
});
GH_AW_MCP_SCRIPTS_SERVER_EOF
- chmod +x /opt/gh-aw/mcp-scripts/mcp-server.cjs
+ chmod +x ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs
- name: Setup MCP Scripts Tool Files
run: |
- cat > /opt/gh-aw/mcp-scripts/qmd-query.sh << 'GH_AW_MCP_SCRIPTS_SH_QMD-QUERY_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/qmd-query.sh << 'GH_AW_MCP_SCRIPTS_SH_QMD-QUERY_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: qmd-query
# Find relevant file paths in project documentation using vector similarity search. Returns file paths and scores.
@@ -811,7 +633,7 @@ jobs:
GH_AW_MCP_SCRIPTS_SH_QMD-QUERY_EOF
- chmod +x /opt/gh-aw/mcp-scripts/qmd-query.sh
+ chmod +x ${GH_AW_HOME}/mcp-scripts/qmd-query.sh
- name: Generate MCP Scripts Server Config
id: mcp-scripts-config
@@ -843,7 +665,7 @@ jobs:
export GH_AW_MCP_SCRIPTS_PORT
export GH_AW_MCP_SCRIPTS_API_KEY
- bash /opt/gh-aw/actions/start_mcp_scripts_server.sh
+ bash ${GH_AW_HOME}/actions/start_mcp_scripts_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -853,7 +675,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -871,19 +694,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"mcpscripts": {
@@ -891,6 +719,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_MCP_SCRIPTS_PORT",
"headers": {
"Authorization": "$GH_AW_MCP_SCRIPTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"safeoutputs": {
@@ -898,6 +733,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -915,7 +757,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -1020,7 +863,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(cat .github/workflows/daily-doc-updater.md),Bash(cat),Bash(date),Bash(echo),Bash(find docs -name '\''\'\'''\''*.md'\''\'\'''\'' -o -name '\''\'\'''\''*.mdx'\''\'\'''\''),Bash(git add:*),Bash(git branch:*),Bash(git checkout:*),Bash(git commit:*),Bash(git diff:*),Bash(git log:*),Bash(git merge:*),Bash(git rm:*),Bash(git show:*),Bash(git status),Bash(git switch:*),Bash(grep),Bash(grep:*),Bash(head),Bash(ls),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1064,15 +907,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -1082,7 +925,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1094,14 +937,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1110,27 +953,27 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Scripts logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_scripts_logs.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_scripts_logs.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1211,9 +1054,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1246,7 +1089,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1274,9 +1117,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1321,6 +1164,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-doc-healer"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1336,7 +1181,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1361,9 +1206,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1375,9 +1220,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1399,9 +1244,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1417,9 +1262,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1432,9 +1277,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
safe_outputs:
@@ -1451,6 +1296,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-doc-healer"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-doc-healer"
GH_AW_WORKFLOW_ID: "daily-doc-healer"
GH_AW_WORKFLOW_NAME: "Daily Documentation Healer"
@@ -1476,7 +1322,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1523,7 +1369,7 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"assignees\":[\"copilot\"],\"expires\":72,\"labels\":[\"documentation\",\"automation\"],\"max\":1,\"title_prefix\":\"[doc-healer] \"},\"create_pull_request\":{\"expires\":72,\"labels\":[\"documentation\",\"automation\"],\"max\":1,\"max_patch_size\":1024,\"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\",\"CLAUDE.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\",\".claude/\"],\"title_prefix\":\"[docs] \"},\"missing_data\":{},\"missing_tool\":{}}"
@@ -1532,9 +1378,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Assign Copilot to created issues
if: steps.process_safe_outputs.outputs.issues_to_assign_copilot != ''
@@ -1544,9 +1390,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/assign_copilot_to_created_issues.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/assign_copilot_to_created_issues.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1563,6 +1409,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: dailydochealer
steps:
- name: Checkout actions folder
@@ -1575,7 +1422,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/daily-doc-healer.md b/.github/workflows/daily-doc-healer.md
index 28a6d2625e2..53fe4722def 100644
--- a/.github/workflows/daily-doc-healer.md
+++ b/.github/workflows/daily-doc-healer.md
@@ -153,7 +153,7 @@ If you make documentation edits, create a pull request with `create_pull_request
**PR Description**:
```markdown
-## Self-Healing Documentation Fixes
+### Self-Healing Documentation Fixes
This PR was automatically created by the Daily Documentation Healer workflow.
@@ -165,10 +165,15 @@ This PR was automatically created by the Daily Documentation Healer workflow.
[Explanation of why DDUw missed this]
+
+💡 DDUw Improvement Suggestions
+
### DDUw Improvement Suggestions
[Specific, actionable changes to daily-doc-updater.md that would prevent recurrence]
+
+
### Related Issues
- Closes #NNN
diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml
index 7596d93b553..b5cd4fc4606 100644
--- a/.github/workflows/daily-doc-updater.lock.yml
+++ b/.github/workflows/daily-doc-updater.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-doc-updater.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,21 +125,21 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -171,6 +171,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -188,9 +189,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -209,10 +210,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -234,11 +235,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -263,10 +264,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailydocupdater
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -287,7 +289,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -298,7 +300,11 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Install QMD
run: npm install -g @tobilu/qmd
- name: Restore QMD index cache
@@ -313,7 +319,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -343,9 +349,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -353,7 +359,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -364,167 +370,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request":{"auto_merge":true,"expires":24,"max":1,"reviewers":["copilot"],"title_prefix":"[docs] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[docs] \". Labels [\"documentation\" \"automation\"] will be automatically added. Reviewers [\"copilot\"] will be assigned.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[docs] \". Labels [\"documentation\" \"automation\"] will be automatically added. Reviewers [\"copilot\"] will be assigned."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -621,6 +490,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -645,8 +515,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -657,16 +527,16 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Setup MCP Scripts Config
run: |
- mkdir -p /opt/gh-aw/mcp-scripts/logs
- cat > /opt/gh-aw/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
+ mkdir -p ${GH_AW_HOME}/mcp-scripts/logs
+ cat > ${GH_AW_HOME}/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
{
"serverName": "mcpscripts",
"version": "1.0.0",
- "logDir": "/opt/gh-aw/mcp-scripts/logs",
+ "logDir": "${GH_AW_HOME}/mcp-scripts/logs",
"tools": [
{
"name": "qmd-query",
@@ -694,7 +564,7 @@ jobs:
]
}
GH_AW_MCP_SCRIPTS_TOOLS_EOF
- cat > /opt/gh-aw/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
const path = require("path");
const { startHttpServer } = require("./mcp_scripts_mcp_server_http.cjs");
const configPath = path.join(__dirname, "tools.json");
@@ -703,17 +573,17 @@ jobs:
startHttpServer(configPath, {
port: port,
stateless: true,
- logDir: "/opt/gh-aw/mcp-scripts/logs"
+ logDir: process.env.GH_AW_HOME + "/mcp-scripts/logs"
}).catch(error => {
console.error("Failed to start mcp-scripts HTTP server:", error);
process.exit(1);
});
GH_AW_MCP_SCRIPTS_SERVER_EOF
- chmod +x /opt/gh-aw/mcp-scripts/mcp-server.cjs
+ chmod +x ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs
- name: Setup MCP Scripts Tool Files
run: |
- cat > /opt/gh-aw/mcp-scripts/qmd-query.sh << 'GH_AW_MCP_SCRIPTS_SH_QMD-QUERY_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/qmd-query.sh << 'GH_AW_MCP_SCRIPTS_SH_QMD-QUERY_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: qmd-query
# Find relevant file paths in project documentation using vector similarity search. Returns file paths and scores.
@@ -725,7 +595,7 @@ jobs:
GH_AW_MCP_SCRIPTS_SH_QMD-QUERY_EOF
- chmod +x /opt/gh-aw/mcp-scripts/qmd-query.sh
+ chmod +x ${GH_AW_HOME}/mcp-scripts/qmd-query.sh
- name: Generate MCP Scripts Server Config
id: mcp-scripts-config
@@ -757,7 +627,7 @@ jobs:
export GH_AW_MCP_SCRIPTS_PORT
export GH_AW_MCP_SCRIPTS_API_KEY
- bash /opt/gh-aw/actions/start_mcp_scripts_server.sh
+ bash ${GH_AW_HOME}/actions/start_mcp_scripts_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -767,7 +637,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -785,19 +656,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"mcpscripts": {
@@ -805,6 +681,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_MCP_SCRIPTS_PORT",
"headers": {
"Authorization": "$GH_AW_MCP_SCRIPTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"safeoutputs": {
@@ -812,6 +695,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -829,7 +719,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -935,7 +826,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(cat pkg/parser/schemas/*.json),Bash(cat),Bash(date),Bash(echo),Bash(find docs -maxdepth 1 -ls),Bash(find docs -name '\''\'\'''\''*.md'\''\'\'''\'' -exec cat {} +),Bash(find docs -name '\''\'\'''\''*.md'\''\'\'''\'' -o -name '\''\'\'''\''*.mdx'\''\'\'''\''),Bash(find pkg/parser/schemas -name '\''\'\'''\''*.json'\''\'\'''\''),Bash(git add:*),Bash(git branch:*),Bash(git checkout:*),Bash(git commit:*),Bash(git merge:*),Bash(git rm:*),Bash(git status),Bash(git switch:*),Bash(git),Bash(grep -r '\''\'\'''\''*'\''\'\'''\'' docs),Bash(grep),Bash(head),Bash(ls),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -979,15 +870,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -997,7 +888,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1009,14 +900,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1025,27 +916,27 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Scripts logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_scripts_logs.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_scripts_logs.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1126,9 +1017,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1161,7 +1052,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1189,9 +1080,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1236,6 +1127,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-doc-updater"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1251,7 +1144,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1276,9 +1169,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1290,9 +1183,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1314,9 +1207,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1332,9 +1225,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1347,9 +1240,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
safe_outputs:
@@ -1366,6 +1259,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-doc-updater"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-doc-updater"
GH_AW_WORKFLOW_ID: "daily-doc-updater"
GH_AW_WORKFLOW_NAME: "Daily Documentation Updater"
@@ -1389,7 +1283,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1436,7 +1330,7 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"auto_merge\":true,\"draft\":false,\"expires\":24,\"labels\":[\"documentation\",\"automation\"],\"max\":1,\"max_patch_size\":1024,\"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\",\"CLAUDE.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\",\".claude/\"],\"reviewers\":[\"copilot\"],\"title_prefix\":\"[docs] \"},\"missing_data\":{},\"missing_tool\":{}}"
@@ -1444,9 +1338,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1463,6 +1357,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: dailydocupdater
steps:
- name: Checkout actions folder
@@ -1475,7 +1370,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/daily-doc-updater.md b/.github/workflows/daily-doc-updater.md
index 81cb2437a13..c3027a77b7a 100644
--- a/.github/workflows/daily-doc-updater.md
+++ b/.github/workflows/daily-doc-updater.md
@@ -187,7 +187,7 @@ If you made any documentation changes:
**PR Description Template**:
```markdown
-## Documentation Updates - [Date]
+### Documentation Updates - [Date]
This PR updates the documentation based on features merged in the last 24 hours.
@@ -196,6 +196,9 @@ This PR updates the documentation based on features merged in the last 24 hours.
- Feature 1 (from #PR_NUMBER)
- Feature 2 (from #PR_NUMBER)
+
+📝 Detailed Changes & References
+
### Changes Made
- Updated `docs/path/to/file.md` to document Feature 1
@@ -206,6 +209,8 @@ This PR updates the documentation based on features merged in the last 24 hours.
- #PR_NUMBER - Brief description
- #PR_NUMBER - Brief description
+
+
### Notes
[Any additional notes or features that need manual review]
diff --git a/.github/workflows/daily-fact.lock.yml b/.github/workflows/daily-fact.lock.yml
index aa8555ba669..b58e62476ad 100644
--- a/.github/workflows/daily-fact.lock.yml
+++ b/.github/workflows/daily-fact.lock.yml
@@ -54,7 +54,7 @@ jobs:
- name: Setup Scripts
uses: github/gh-aw-actions/setup@c303e453d96fe6789ee8cb3d63033c710eac347a # v0
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -69,18 +69,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate CODEX_API_KEY or OPENAI_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
@@ -90,9 +90,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-fact.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -107,15 +107,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, missing_tool, missing_data, noop
@@ -149,6 +149,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -230,9 +231,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -248,10 +249,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -270,11 +271,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -301,10 +302,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyfact
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -318,13 +320,17 @@ jobs:
- name: Setup Scripts
uses: github/gh-aw-actions/setup@c303e453d96fe6789ee8cb3d63033c710eac347a # v0
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -347,9 +353,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -359,7 +365,7 @@ jobs:
- name: Install Codex
run: npm install -g @openai/codex@latest
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -368,155 +374,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":1,"target":"4750"},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added. Target: 4750.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Target: 4750."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -595,6 +476,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -619,8 +501,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -631,7 +513,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -639,7 +521,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -657,7 +540,7 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="codex"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
cat > /tmp/gh-aw/mcp-config/config.toml << GH_AW_MCP_CONFIG_EOF
[history]
@@ -681,20 +564,30 @@ jobs:
[mcp_servers.safeoutputs.headers]
Authorization = "$GH_AW_SAFE_OUTPUTS_API_KEY"
+
+ [mcp_servers.safeoutputs."guard-policies"]
+
+ [mcp_servers.safeoutputs."guard-policies".write-sink]
+ accept = ["*"]
GH_AW_MCP_CONFIG_EOF
# Generate JSON config for MCP gateway
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,discussions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -702,6 +595,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -719,13 +619,14 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Codex
run: |
set -o pipefail
mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" && codex ${GH_AW_MODEL_AGENT_CODEX:+-c model="$GH_AW_MODEL_AGENT_CODEX" }exec -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
@@ -764,15 +665,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'CODEX_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY'
@@ -783,7 +684,7 @@ jobs:
SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -800,9 +701,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -811,18 +712,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_codex_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_codex_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -897,9 +798,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -912,7 +813,7 @@ jobs:
set -o pipefail
mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,host.docker.internal,openai.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,host.docker.internal,openai.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" && codex ${GH_AW_MODEL_DETECTION_CODEX:+-c model="$GH_AW_MODEL_DETECTION_CODEX" }exec -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
@@ -936,9 +837,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -983,6 +884,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-fact"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -991,7 +894,7 @@ jobs:
- name: Setup Scripts
uses: github/gh-aw-actions/setup@c303e453d96fe6789ee8cb3d63033c710eac347a # v0
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1016,9 +919,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1030,9 +933,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1053,9 +956,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1071,9 +974,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1090,6 +993,7 @@ jobs:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-fact"
GH_AW_ENGINE_ID: "codex"
GH_AW_ENGINE_MODEL: "gpt-5.1-codex-mini"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🪶 *Penned with care by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"📜 Hark! The muse awakens — [{workflow_name}]({run_url}) begins its verse upon this {event_type}...\",\"runSuccess\":\"✨ Lo! [{workflow_name}]({run_url}) hath woven its tale to completion, like a sonnet finding its final rhyme. 🌟\",\"runFailure\":\"🌧️ Alas! [{workflow_name}]({run_url}) {status}, its quill fallen mid-verse. The poem remains unfinished...\"}"
GH_AW_TRACKER_ID: "daily-fact-thread"
GH_AW_WORKFLOW_ID: "daily-fact"
@@ -1107,7 +1011,7 @@ jobs:
- name: Setup Scripts
uses: github/gh-aw-actions/setup@c303e453d96fe6789ee8cb3d63033c710eac347a # v0
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1133,9 +1037,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-file-diet.lock.yml b/.github/workflows/daily-file-diet.lock.yml
index cf66e4345fd..1ab617a329c 100644
--- a/.github/workflows/daily-file-diet.lock.yml
+++ b/.github/workflows/daily-file-diet.lock.yml
@@ -30,7 +30,7 @@
# - shared/reporting.md
# - shared/safe-output-app.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"2bf96ef660042f766ad92ab43f4a2b2e74bc1b62ad74c95679bed28e6fe4ce75","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"e1feeb8baad015890ddef8ed2629ed59e20fd52a2b4a963f673f2395bd1a3750","strict":true}
name: "Daily File Diet"
"on":
@@ -68,7 +68,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -83,14 +83,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -107,9 +107,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-file-diet.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -124,15 +124,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -166,6 +166,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -193,9 +194,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -212,10 +213,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -235,11 +236,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -265,10 +266,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyfilediet
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -290,13 +292,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -319,16 +325,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -337,167 +343,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":48,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[file-diet] \". Labels [\"refactoring\" \"code-health\" \"automated-analysis\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[file-diet] \". Labels [\"refactoring\" \"code-health\" \"automated-analysis\" \"cookie\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -591,6 +460,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -615,8 +485,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -627,7 +497,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -635,7 +505,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -653,10 +524,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -664,10 +535,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -675,6 +551,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -683,7 +566,14 @@ jobs:
"args": ["--network", "host"],
"entrypoint": "serena",
"entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -700,7 +590,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -731,7 +622,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool serena --allow-tool '\''shell(cat pkg/**/*.go)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find pkg -name '\''\'\'''\''*.go'\''\'\'''\'' ! -name '\''\'\'''\''*_test.go'\''\'\'''\'' -type f -exec wc -l {} \; | sort -rn)'\'' --allow-tool '\''shell(find pkg/ -maxdepth 1 -ls)'\'' --allow-tool '\''shell(grep -r '\''\'\'''\''func '\''\'\'''\'' pkg --include='\''\'\'''\''*.go'\''\'\'''\'')'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head -n * pkg/**/*.go)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc -l pkg/**/*.go)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -760,7 +651,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -798,15 +689,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -815,7 +706,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -832,9 +723,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -843,18 +734,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -929,9 +820,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -954,7 +845,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -982,9 +873,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1027,6 +918,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-file-diet"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1042,7 +935,18 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
+ - name: Generate GitHub App token
+ id: safe-outputs-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
+ permission-contents: read
+ permission-issues: write
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1065,11 +969,11 @@ jobs:
GH_AW_WORKFLOW_NAME: "Daily File Diet"
GH_AW_TRACKER_ID: "daily-file-diet"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1079,11 +983,11 @@ jobs:
GH_AW_WORKFLOW_NAME: "Daily File Diet"
GH_AW_TRACKER_ID: "daily-file-diet"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1101,11 +1005,11 @@ jobs:
GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
GH_AW_TIMEOUT_MINUTES: "20"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1119,12 +1023,25 @@ jobs:
GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }}
GH_AW_NOOP_REPORT_AS_ISSUE: "true"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
+ - name: Invalidate GitHub App token
+ if: always() && steps.safe-outputs-app-token.outputs.token != ''
+ env:
+ TOKEN: ${{ steps.safe-outputs-app-token.outputs.token }}
+ run: |
+ echo "Revoking GitHub App installation token..."
+ # GitHub CLI will auth with the token being revoked.
+ gh api \
+ --method DELETE \
+ -H "Authorization: token $TOKEN" \
+ /installation/token || echo "Token revoke may already be expired."
+
+ echo "Token invalidation step complete."
pre_activation:
runs-on: ubuntu-slim
@@ -1144,7 +1061,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1153,10 +1070,19 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
+ - name: Generate GitHub App token for skip-if checks
+ id: pre-activation-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
- name: Check skip-if-match query
id: check_skip_if_match
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1165,10 +1091,11 @@ jobs:
GH_AW_WORKFLOW_NAME: "Daily File Diet"
GH_AW_SKIP_MAX_MATCHES: "1"
with:
+ github-token: ${{ steps.pre-activation-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_skip_if_match.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_skip_if_match.cjs');
await main();
safe_outputs:
@@ -1182,6 +1109,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-file-diet"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-file-diet"
GH_AW_WORKFLOW_ID: "daily-file-diet"
GH_AW_WORKFLOW_NAME: "Daily File Diet"
@@ -1205,7 +1133,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1219,6 +1147,17 @@ jobs:
mkdir -p /tmp/gh-aw/
find "/tmp/gh-aw/" -type f -print
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV"
+ - name: Generate GitHub App token
+ id: safe-outputs-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
+ permission-contents: read
+ permission-issues: write
- name: Process Safe Outputs
id: process_safe_outputs
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1229,12 +1168,25 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"expires\":48,\"labels\":[\"refactoring\",\"code-health\",\"automated-analysis\",\"cookie\"],\"max\":1,\"title_prefix\":\"[file-diet] \"},\"missing_data\":{},\"missing_tool\":{}}"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
+ - name: Invalidate GitHub App token
+ if: always() && steps.safe-outputs-app-token.outputs.token != ''
+ env:
+ TOKEN: ${{ steps.safe-outputs-app-token.outputs.token }}
+ run: |
+ echo "Revoking GitHub App installation token..."
+ # GitHub CLI will auth with the token being revoked.
+ gh api \
+ --method DELETE \
+ -H "Authorization: token $TOKEN" \
+ /installation/token || echo "Token revoke may already be expired."
+
+ echo "Token invalidation step complete."
- name: Upload Safe Output Items Manifest
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
diff --git a/.github/workflows/daily-firewall-report.lock.yml b/.github/workflows/daily-firewall-report.lock.yml
index 67a49718c7a..87fafc0aab5 100644
--- a/.github/workflows/daily-firewall-report.lock.yml
+++ b/.github/workflows/daily-firewall-report.lock.yml
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,18 +80,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -109,9 +109,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-firewall-report.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -126,16 +126,17 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, upload_asset, missing_tool, missing_data, noop
@@ -171,6 +172,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -190,9 +192,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -211,10 +213,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -236,11 +238,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -268,10 +270,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyfirewallreport
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -293,7 +296,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -329,7 +332,11 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup Python environment
run: |
mkdir -p /tmp/gh-aw/python/{data,charts,artifacts}
@@ -355,7 +362,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -385,16 +392,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -403,10 +410,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -421,183 +428,37 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -693,6 +554,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -717,8 +579,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -729,7 +591,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -740,7 +602,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -759,10 +622,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -775,6 +638,13 @@ jobs:
"GITHUB_TOKEN": "\${GITHUB_TOKEN}",
"GITHUB_ACTOR": "\${GITHUB_ACTOR}",
"GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
@@ -782,10 +652,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "all"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -793,6 +668,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -810,7 +692,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -819,7 +702,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,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,files.pythonhosted.org,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,static.crates.io,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,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,files.pythonhosted.org,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,static.crates.io,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -850,7 +733,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -888,15 +771,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -906,7 +789,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -923,9 +806,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -934,18 +817,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1035,9 +918,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1060,7 +943,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1087,9 +970,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1135,6 +1018,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-firewall-report"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1150,7 +1035,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1175,9 +1060,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1189,9 +1074,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1214,9 +1099,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1232,9 +1117,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1249,6 +1134,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-firewall-report"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-firewall-report"
GH_AW_WORKFLOW_ID: "daily-firewall-report"
GH_AW_WORKFLOW_NAME: "Daily Firewall Logs Collector and Reporter"
@@ -1270,7 +1156,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1296,9 +1182,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1315,6 +1201,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: dailyfirewallreport
steps:
- name: Checkout actions folder
@@ -1327,7 +1214,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1372,7 +1259,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1428,8 +1315,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/daily-firewall-report.md b/.github/workflows/daily-firewall-report.md
index b4e87394a6f..cb6d5d9f5c4 100644
--- a/.github/workflows/daily-firewall-report.md
+++ b/.github/workflows/daily-firewall-report.md
@@ -112,7 +112,7 @@ Generate exactly **2 high-quality trend charts**:
Include the charts in your firewall report with this structure:
```markdown
-## 📈 Firewall Activity Trends
+### 📈 Firewall Activity Trends
### Request Patterns

diff --git a/.github/workflows/daily-function-namer.lock.yml b/.github/workflows/daily-function-namer.lock.yml
index 3c791822e5c..0056eef61e9 100644
--- a/.github/workflows/daily-function-namer.lock.yml
+++ b/.github/workflows/daily-function-namer.lock.yml
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,18 +80,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -109,9 +109,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-function-namer.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -126,16 +126,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -169,6 +169,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -191,9 +192,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -212,10 +213,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -237,11 +238,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -266,10 +267,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyfunctionnamer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -290,16 +292,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -329,9 +335,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -339,7 +345,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -350,167 +356,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":168,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[function-namer] \". Labels [\"refactoring\" \"code-quality\" \"automated-analysis\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[function-namer] \". Labels [\"refactoring\" \"code-quality\" \"automated-analysis\" \"cookie\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -604,6 +473,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -628,8 +498,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -640,7 +510,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -648,7 +518,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -666,19 +537,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -686,6 +562,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -702,7 +585,14 @@ jobs:
"--project",
"\${GITHUB_WORKSPACE}"
],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -719,7 +609,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -811,7 +702,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(cat),Bash(date),Bash(echo),Bash(find pkg -name '\''\'\'''\''*.go'\''\'\'''\'' ! -name '\''\'\'''\''*_test.go'\''\'\'''\'' -type f | sort),Bash(grep),Bash(head),Bash(ls),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -855,15 +746,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -873,7 +764,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -890,9 +781,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -901,18 +792,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -991,9 +882,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1026,7 +917,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1054,9 +945,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1100,6 +991,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-function-namer"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1115,7 +1008,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1140,9 +1033,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1154,9 +1047,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1176,9 +1069,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1194,9 +1087,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1210,6 +1103,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-function-namer"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-function-namer"
GH_AW_WORKFLOW_ID: "daily-function-namer"
GH_AW_WORKFLOW_NAME: "Daily Go Function Namer"
@@ -1233,7 +1127,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1259,9 +1153,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1278,6 +1172,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: dailyfunctionnamer
steps:
- name: Checkout actions folder
@@ -1290,7 +1185,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/daily-function-namer.md b/.github/workflows/daily-function-namer.md
index 2919b429d1f..1db8cd23cb9 100644
--- a/.github/workflows/daily-function-namer.md
+++ b/.github/workflows/daily-function-namer.md
@@ -227,15 +227,15 @@ Otherwise, create an issue with this structure:
**Analysis Date**:
**Round-Robin Position**: files – of total
-## Why This Matters
+### Why This Matters
When AI coding agents search for functions to complete a task, they rely on function
names to understand what code does. Clear, descriptive names increase the likelihood
that an agent will find the right function instead of reimplementing existing logic.
-## Rename Suggestions
+### Rename Suggestions
-### ``
+#### ``
| Current Name | Suggested Name | Reason |
|---|---|---|
@@ -245,17 +245,20 @@ that an agent will find the right function instead of reimplementing existing lo
- `functionA()` — ✅ Clear, no change needed
- `oldName()` — ⚠️ Rename suggested (see table above)
-### ``
+#### ``
-### ``
+#### ``
---
-## Agentic Implementation Plan
+
+🤖 Agentic Implementation Plan
+
+### Agentic Implementation Plan
This issue is designed to be assigned to a coding agent. The agent should implement
all rename suggestions below in a single pull request.
@@ -331,6 +334,8 @@ refactor: rename to for clarity
- Follow existing naming conventions documented in `AGENTS.md`
- Unexported functions used only as closures or immediately-invoked can be skipped
+
+
---
*Generated by the Daily Go Function Namer workflow*
diff --git a/.github/workflows/daily-issues-report.lock.yml b/.github/workflows/daily-issues-report.lock.yml
index 48a60b66fc6..1a36026cda3 100644
--- a/.github/workflows/daily-issues-report.lock.yml
+++ b/.github/workflows/daily-issues-report.lock.yml
@@ -28,10 +28,11 @@
# - shared/issues-data-fetch.md
# - shared/jqschema.md
# - shared/python-dataviz.md
+# - shared/python-nlp.md
# - shared/reporting.md
# - shared/trends.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"667a196ac2033e3f747d5c0dd2d3391c9a05f01941879279691b28a1ccc5f68b","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"0b3fc119e1d72b7a68ab71c7298b846813cae152c54e8384de51cce919de9675","strict":true}
name: "Daily Issues Report Generator"
"on":
@@ -70,7 +71,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -85,7 +86,7 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
@@ -95,11 +96,11 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate CODEX_API_KEY or OPENAI_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
@@ -118,9 +119,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-issues-report.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -135,16 +136,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, close_discussion, upload_asset, missing_tool, missing_data, noop
@@ -180,6 +181,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -193,6 +195,9 @@ jobs:
{{#runtime-import .github/workflows/shared/python-dataviz.md}}
GH_AW_PROMPT_EOF
cat << 'GH_AW_PROMPT_EOF'
+ {{#runtime-import .github/workflows/shared/python-nlp.md}}
+ GH_AW_PROMPT_EOF
+ cat << 'GH_AW_PROMPT_EOF'
{{#runtime-import .github/workflows/shared/trends.md}}
GH_AW_PROMPT_EOF
cat << 'GH_AW_PROMPT_EOF'
@@ -210,9 +215,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -232,10 +237,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -258,11 +263,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -289,10 +294,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyissuesreport
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -313,13 +319,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
- env:
@@ -349,10 +359,12 @@ jobs:
/tmp/gh-aw/python/*.py
/tmp/gh-aw/python/data/*
retention-days: 30
+ - name: Setup Python NLP environment
+ run: "mkdir -p /tmp/gh-aw/python/{data,charts,artifacts}\npip install --user --quiet nltk scikit-learn textblob wordcloud\n\n# Download required NLTK corpora\npython3 -c \"\nimport nltk\nfor corpus in ['punkt_tab', 'stopwords', 'vader_lexicon', 'averaged_perceptron_tagger_eng']:\n nltk.download(corpus, quiet=True)\nprint('NLTK corpora ready')\n\"\n\npython3 -c \"import sklearn; print(f'scikit-learn {sklearn.__version__}')\""
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -382,9 +394,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -394,218 +406,41 @@ jobs:
- name: Install Codex
run: npm install -g @openai/codex@latest
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"close_discussion":{"max":10},"create_discussion":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[daily issues] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Close a GitHub discussion with a resolution comment and optional reason. You can and should always add a comment when closing a discussion to explain the action or provide context. Use this to mark discussions as resolved, answered, or no longer needed. The closing comment should explain why the discussion is being closed. If the discussion is already closed, a comment will still be posted. CONSTRAINTS: Maximum 10 discussion(s) can be closed.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Closing comment explaining why the discussion is being closed and summarizing any resolution or conclusion.",
- "type": "string"
- },
- "discussion_number": {
- "description": "Discussion number to close. This is the numeric ID from the GitHub URL (e.g., 678 in github.com/owner/repo/discussions/678). If omitted, closes the discussion that triggered this workflow (requires a discussion event trigger).",
- "type": [
- "number",
- "string"
- ]
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Resolution reason: RESOLVED (issue addressed), DUPLICATE (discussed elsewhere), OUTDATED (no longer relevant), or ANSWERED (question answered).",
- "enum": [
- "RESOLVED",
- "DUPLICATE",
- "OUTDATED",
- "ANSWERED"
- ],
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "close_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "close_discussion": " CONSTRAINTS: Maximum 10 discussion(s) can be closed.",
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[daily issues] \". Discussions will be created in category \"audits\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"close_discussion": {
"defaultMax": 1,
@@ -728,6 +563,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -752,8 +588,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -764,7 +600,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -775,6 +611,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -792,7 +630,7 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="codex"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
cat > /tmp/gh-aw/mcp-config/config.toml << GH_AW_MCP_CONFIG_EOF
[history]
@@ -816,10 +654,15 @@ jobs:
[mcp_servers.safeoutputs.headers]
Authorization = "$GH_AW_SAFE_OUTPUTS_API_KEY"
+
+ [mcp_servers.safeoutputs."guard-policies"]
+
+ [mcp_servers.safeoutputs."guard-policies".write-sink]
+ accept = ["*"]
GH_AW_MCP_CONFIG_EOF
# Generate JSON config for MCP gateway
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -830,6 +673,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,discussions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -837,6 +686,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -854,13 +710,14 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Codex
run: |
set -o pipefail
mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.pythonhosted.org,172.30.0.1,anaconda.org,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,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,files.pythonhosted.org,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,static.crates.io,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.pythonhosted.org,172.30.0.1,anaconda.org,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,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,files.pythonhosted.org,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,static.crates.io,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" && codex ${GH_AW_MODEL_AGENT_CODEX:+-c model="$GH_AW_MODEL_AGENT_CODEX" }exec -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
@@ -902,15 +759,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'CODEX_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY'
@@ -921,7 +778,7 @@ jobs:
SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -938,9 +795,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -949,18 +806,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_codex_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_codex_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1050,9 +907,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1065,7 +922,7 @@ jobs:
set -o pipefail
mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,host.docker.internal,openai.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,host.docker.internal,openai.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" && codex ${GH_AW_MODEL_DETECTION_CODEX:+-c model="$GH_AW_MODEL_DETECTION_CODEX" }exec -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
@@ -1089,9 +946,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1137,6 +994,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-issues-report"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1152,7 +1011,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1177,9 +1036,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1191,9 +1050,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1215,9 +1074,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1233,9 +1092,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
pre_activation:
@@ -1256,7 +1115,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1265,9 +1124,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
safe_outputs:
@@ -1282,6 +1141,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-issues-report"
GH_AW_ENGINE_ID: "codex"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-issues-report"
GH_AW_WORKFLOW_ID: "daily-issues-report"
GH_AW_WORKFLOW_NAME: "Daily Issues Report Generator"
@@ -1303,7 +1163,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1329,9 +1189,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1348,6 +1208,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: dailyissuesreport
steps:
- name: Checkout actions folder
@@ -1360,7 +1221,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1405,7 +1266,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1461,8 +1322,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/daily-issues-report.md b/.github/workflows/daily-issues-report.md
index c562df375f8..e0b44b34f21 100644
--- a/.github/workflows/daily-issues-report.md
+++ b/.github/workflows/daily-issues-report.md
@@ -29,6 +29,7 @@ imports:
- shared/jqschema.md
- shared/issues-data-fetch.md
- shared/python-dataviz.md
+ - shared/python-nlp.md
- shared/trends.md
- shared/reporting.md
---
@@ -190,10 +191,7 @@ with open('/tmp/gh-aw/python/data/metrics.json', 'w') as f:
### Install Additional Libraries
-If needed for better clustering:
-```bash
-pip install --user scikit-learn
-```
+scikit-learn, NLTK, TextBlob, and WordCloud are pre-installed via the shared NLP environment.
## Phase 3: Generate Trend Charts
diff --git a/.github/workflows/daily-malicious-code-scan.lock.yml b/.github/workflows/daily-malicious-code-scan.lock.yml
index 415b5fa9354..bb556b1bfdd 100644
--- a/.github/workflows/daily-malicious-code-scan.lock.yml
+++ b/.github/workflows/daily-malicious-code-scan.lock.yml
@@ -63,7 +63,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -78,14 +78,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -102,9 +102,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-malicious-code-scan.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -119,15 +119,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_code_scanning_alert, missing_tool, missing_data, noop
@@ -161,6 +161,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -178,9 +179,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -196,10 +197,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -218,11 +219,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -248,10 +249,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailymaliciouscodescan
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -271,13 +273,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -300,16 +306,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -318,178 +324,28 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_code_scanning_alert":{"max":0},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a code scanning alert for security vulnerabilities, code quality issues, or other findings. Alerts appear in the repository's Security tab and integrate with GitHub's security features. Use this for automated security analysis results.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "column": {
- "description": "Column number for more precise location of the issue within the line.",
- "type": [
- "number",
- "string"
- ]
- },
- "file": {
- "description": "File path relative to the repository root where the issue was found (e.g., 'src/auth/password.js').",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "line": {
- "description": "Line number where the issue was found in the file.",
- "type": [
- "number",
- "string"
- ]
- },
- "message": {
- "description": "Clear description of the security issue or finding. Include what's wrong and ideally how to fix it.",
- "type": "string"
- },
- "ruleIdSuffix": {
- "description": "Suffix to append to the rule ID for categorizing different types of findings (e.g., 'sql-injection', 'xss').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "severity": {
- "description": "Alert severity level: 'error' (critical security issues), 'warning' (potential problems), 'info' (informational), or 'note' (minor observations).",
- "enum": [
- "error",
- "warning",
- "info",
- "note"
- ],
- "type": "string"
- }
- },
- "required": [
- "file",
- "line",
- "severity",
- "message"
- ],
- "type": "object"
- },
- "name": "create_code_scanning_alert"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {},
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_code_scanning_alert": {
"defaultMax": 40,
@@ -591,6 +447,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -615,8 +472,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -627,7 +484,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -635,7 +492,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -653,10 +511,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -664,10 +522,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "repos,code_security"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -675,6 +538,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -692,7 +562,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -701,7 +572,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -730,7 +601,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -768,15 +639,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -785,7 +656,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -802,9 +673,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -813,18 +684,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -872,6 +743,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-malicious-code-scan"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -887,7 +760,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -912,9 +785,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -926,9 +799,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -948,9 +821,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -966,9 +839,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -982,6 +855,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-malicious-code-scan"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "malicious-code-scan"
GH_AW_WORKFLOW_ID: "daily-malicious-code-scan"
GH_AW_WORKFLOW_NAME: "Daily Malicious Code Scan Agent"
@@ -1003,7 +877,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1029,9 +903,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml
index b191750a898..1b21195bf6b 100644
--- a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml
+++ b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml
@@ -28,7 +28,7 @@
# - shared/reporting.md
# - shared/safe-output-app.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"595d7d1155099c3a8595cae572c2112d06f01a59698ce96ddeccd87f1da82d96","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"0d1523c4f840b4f4ce87ca5b75e252c3ed7bfb3b11cce78987b796655a3bff81","strict":true}
name: "Daily MCP Tool Concurrency Analysis"
"on":
@@ -63,7 +63,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -78,14 +78,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -102,9 +102,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-mcp-concurrency-analysis.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -119,16 +119,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, create_agent_session, missing_tool, missing_data, noop
@@ -162,6 +162,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -183,9 +184,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -204,10 +205,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -229,11 +230,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -259,10 +260,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailymcpconcurrencyanalysis
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -284,16 +286,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -323,16 +329,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -341,192 +347,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_agent_session":{"max":3},"create_issue":{"expires":168,"max":5},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 5 issue(s) can be created. Title will be prefixed with \"[concurrency] \". Labels [\"bug\" \"concurrency\" \"thread-safety\" \"automated-analysis\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Create a GitHub Copilot coding agent session to delegate coding work. Use this when you need another Copilot coding agent to implement code changes, fix bugs, or complete development tasks. The task becomes a new issue that triggers the Copilot coding agent. For non-coding tasks or manual work items, use create_issue instead. CONSTRAINTS: Maximum 3 agent task(s) can be created.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Clear, detailed task description for the Copilot coding agent. Include specific files to modify, expected behavior, acceptance criteria, and any constraints. The description should be actionable and self-contained.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "create_agent_session"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_agent_session": " CONSTRAINTS: Maximum 3 agent task(s) can be created.",
+ "create_issue": " CONSTRAINTS: Maximum 5 issue(s) can be created. Title will be prefixed with \"[concurrency] \". Labels [\"bug\" \"concurrency\" \"thread-safety\" \"automated-analysis\" \"cookie\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_agent_session": {
"defaultMax": 1,
@@ -635,6 +480,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -659,8 +505,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -671,7 +517,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -679,7 +525,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -697,10 +544,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -708,10 +555,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -719,6 +571,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -727,7 +586,14 @@ jobs:
"args": ["--network", "host"],
"entrypoint": "serena",
"entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -744,7 +610,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -775,7 +642,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool serena --allow-tool '\''shell(cat actions/setup/js/*.cjs)'\'' --allow-tool '\''shell(cat pkg/workflow/js/safe_outputs_tools.json)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find actions/setup/js -name '\''\'\'''\''*.cjs'\''\'\'''\'' ! -name '\''\'\'''\''*.test.cjs'\''\'\'''\'' -type f)'\'' --allow-tool '\''shell(grep -r '\''\'\'''\''let \|var \|const '\''\'\'''\'' actions/setup/js --include='\''\'\'''\''*.cjs'\''\'\'''\'')'\'' --allow-tool '\''shell(grep -r '\''\'\'''\''module.exports'\''\'\'''\'' actions/setup/js --include='\''\'\'''\''*.cjs'\''\'\'''\'')'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head -n * actions/setup/js/*.cjs)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -804,7 +671,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -842,15 +709,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -859,7 +726,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -876,9 +743,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -887,18 +754,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -979,9 +846,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1004,7 +871,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1032,9 +899,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1078,6 +945,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-mcp-concurrency-analysis"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1093,7 +962,18 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
+ - name: Generate GitHub App token
+ id: safe-outputs-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
+ permission-contents: read
+ permission-issues: write
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1116,11 +996,11 @@ jobs:
GH_AW_WORKFLOW_NAME: "Daily MCP Tool Concurrency Analysis"
GH_AW_TRACKER_ID: "mcp-concurrency-analysis"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1130,11 +1010,11 @@ jobs:
GH_AW_WORKFLOW_NAME: "Daily MCP Tool Concurrency Analysis"
GH_AW_TRACKER_ID: "mcp-concurrency-analysis"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1152,11 +1032,11 @@ jobs:
GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
GH_AW_TIMEOUT_MINUTES: "45"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1170,12 +1050,25 @@ jobs:
GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }}
GH_AW_NOOP_REPORT_AS_ISSUE: "true"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
+ - name: Invalidate GitHub App token
+ if: always() && steps.safe-outputs-app-token.outputs.token != ''
+ env:
+ TOKEN: ${{ steps.safe-outputs-app-token.outputs.token }}
+ run: |
+ echo "Revoking GitHub App installation token..."
+ # GitHub CLI will auth with the token being revoked.
+ gh api \
+ --method DELETE \
+ -H "Authorization: token $TOKEN" \
+ /installation/token || echo "Token revoke may already be expired."
+
+ echo "Token invalidation step complete."
safe_outputs:
needs: agent
@@ -1188,6 +1081,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-mcp-concurrency-analysis"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "mcp-concurrency-analysis"
GH_AW_WORKFLOW_ID: "daily-mcp-concurrency-analysis"
GH_AW_WORKFLOW_NAME: "Daily MCP Tool Concurrency Analysis"
@@ -1213,7 +1107,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1227,6 +1121,17 @@ jobs:
mkdir -p /tmp/gh-aw/
find "/tmp/gh-aw/" -type f -print
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV"
+ - name: Generate GitHub App token
+ id: safe-outputs-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
+ permission-contents: read
+ permission-issues: write
- name: Process Safe Outputs
id: process_safe_outputs
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1237,11 +1142,11 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_agent_session\":{\"max\":3},\"create_issue\":{\"expires\":168,\"labels\":[\"bug\",\"concurrency\",\"thread-safety\",\"automated-analysis\",\"cookie\"],\"max\":5,\"title_prefix\":\"[concurrency] \"},\"missing_data\":{},\"missing_tool\":{}}"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Create Agent Session
id: create_agent_session
@@ -1250,11 +1155,24 @@ jobs:
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
with:
- github-token: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/create_agent_session.cjs'); await main();
+ const { main } = require(process.env.GH_AW_HOME + '/actions/create_agent_session.cjs'); await main();
+ - name: Invalidate GitHub App token
+ if: always() && steps.safe-outputs-app-token.outputs.token != ''
+ env:
+ TOKEN: ${{ steps.safe-outputs-app-token.outputs.token }}
+ run: |
+ echo "Revoking GitHub App installation token..."
+ # GitHub CLI will auth with the token being revoked.
+ gh api \
+ --method DELETE \
+ -H "Authorization: token $TOKEN" \
+ /installation/token || echo "Token revoke may already be expired."
+
+ echo "Token invalidation step complete."
- name: Upload Safe Output Items Manifest
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -1270,6 +1188,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: dailymcpconcurrencyanalysis
steps:
- name: Checkout actions folder
@@ -1282,7 +1201,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/daily-multi-device-docs-tester.lock.yml b/.github/workflows/daily-multi-device-docs-tester.lock.yml
index b87a20a4f35..fb630ea4da4 100644
--- a/.github/workflows/daily-multi-device-docs-tester.lock.yml
+++ b/.github/workflows/daily-multi-device-docs-tester.lock.yml
@@ -28,7 +28,7 @@
# - shared/docs-server-lifecycle.md
# - shared/reporting.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"8f84bb2f90e6a52e64d8b619fa2b429f3d8bc1150ab7268f650c93dca9c16473","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"f5a4802459021b8361db58fb67d33ae472aaffe53fceda01e56ad10d735f2c27","strict":true}
name: "Multi-Device Docs Tester"
"on":
@@ -70,7 +70,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -85,18 +85,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["node"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -114,9 +114,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-multi-device-docs-tester.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -132,16 +132,16 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_INPUTS_DEVICES: ${{ inputs.devices }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/playwright_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/playwright_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, upload_asset, missing_tool, missing_data, noop
@@ -177,6 +177,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -201,9 +202,9 @@ jobs:
GH_AW_INPUTS_DEVICES: ${{ inputs.devices }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -220,10 +221,10 @@ jobs:
GH_AW_INPUTS_DEVICES: ${{ inputs.devices }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -243,11 +244,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -272,10 +273,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailymultidevicedocstester
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -296,13 +298,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -325,9 +331,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -335,7 +341,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -346,192 +352,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcr.microsoft.com/playwright/mcp node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcr.microsoft.com/playwright/mcp node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":48,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Labels [\"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Labels [\"cookie\"] will be automatically added.",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -634,6 +479,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -658,8 +504,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -670,7 +516,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -681,7 +527,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -700,19 +547,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"playwright": {
@@ -730,13 +582,27 @@ jobs:
"/tmp/gh-aw/mcp-logs/playwright",
"--no-sandbox"
],
- "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"]
+ "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"safeoutputs": {
"type": "http",
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -754,7 +620,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -770,8 +637,8 @@ jobs:
# - Bash(ls*)
# - Bash(lsof*)
# - Bash(npm install*)
- # - Bash(npm run build*)
- # - Bash(npm run preview*)
+ # - Bash(npm run dev*)
+ # - Bash(npx astro*)
# - Bash(npx playwright*)
# - Bash(pwd)
# - Bash(pwd*)
@@ -872,24 +739,25 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.jsr.io,anthropic.com,api.anthropic.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,skimdb.npmjs.com,statsig.anthropic.com,storage.googleapis.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
- -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --max-turns 30 --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(cat),Bash(cd*),Bash(curl*),Bash(date),Bash(echo),Bash(grep),Bash(head),Bash(kill*),Bash(ls),Bash(ls*),Bash(lsof*),Bash(npm install*),Bash(npm run build*),Bash(npm run preview*),Bash(npx playwright*),Bash(pwd),Bash(pwd*),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users,mcp__playwright__browser_click,mcp__playwright__browser_close,mcp__playwright__browser_console_messages,mcp__playwright__browser_drag,mcp__playwright__browser_evaluate,mcp__playwright__browser_file_upload,mcp__playwright__browser_fill_form,mcp__playwright__browser_handle_dialog,mcp__playwright__browser_hover,mcp__playwright__browser_install,mcp__playwright__browser_navigate,mcp__playwright__browser_navigate_back,mcp__playwright__browser_network_requests,mcp__playwright__browser_press_key,mcp__playwright__browser_resize,mcp__playwright__browser_select_option,mcp__playwright__browser_snapshot,mcp__playwright__browser_tabs,mcp__playwright__browser_take_screenshot,mcp__playwright__browser_type,mcp__playwright__browser_wait_for'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.jsr.io,anthropic.com,api.anthropic.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,skimdb.npmjs.com,statsig.anthropic.com,storage.googleapis.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
+ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --max-turns 80 --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(cat),Bash(cd*),Bash(curl*),Bash(date),Bash(echo),Bash(grep),Bash(head),Bash(kill*),Bash(ls),Bash(ls*),Bash(lsof*),Bash(npm install*),Bash(npm run dev*),Bash(npx astro*),Bash(npx playwright*),Bash(pwd),Bash(pwd*),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users,mcp__playwright__browser_click,mcp__playwright__browser_close,mcp__playwright__browser_console_messages,mcp__playwright__browser_drag,mcp__playwright__browser_evaluate,mcp__playwright__browser_file_upload,mcp__playwright__browser_fill_form,mcp__playwright__browser_handle_dialog,mcp__playwright__browser_hover,mcp__playwright__browser_install,mcp__playwright__browser_navigate,mcp__playwright__browser_navigate_back,mcp__playwright__browser_network_requests,mcp__playwright__browser_press_key,mcp__playwright__browser_resize,mcp__playwright__browser_select_option,mcp__playwright__browser_snapshot,mcp__playwright__browser_tabs,mcp__playwright__browser_take_screenshot,mcp__playwright__browser_type,mcp__playwright__browser_wait_for'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- BASH_DEFAULT_TIMEOUT_MS: 60000
- BASH_MAX_TIMEOUT_MS: 60000
+ BASH_DEFAULT_TIMEOUT_MS: 120000
+ BASH_MAX_TIMEOUT_MS: 120000
DISABLE_BUG_COMMAND: 1
DISABLE_ERROR_REPORTING: 1
DISABLE_TELEMETRY: 1
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
- GH_AW_MAX_TURNS: 30
+ GH_AW_MAX_TURNS: 80
GH_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json
GH_AW_MODEL_AGENT_CLAUDE: ${{ vars.GH_AW_MODEL_AGENT_CLAUDE || '' }}
GH_AW_PHASE: agent
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_TOOL_TIMEOUT: 120
GH_AW_VERSION: dev
GITHUB_AW: true
GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
@@ -899,7 +767,7 @@ jobs:
GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
GIT_COMMITTER_NAME: github-actions[bot]
MCP_TIMEOUT: 120000
- MCP_TOOL_TIMEOUT: 60000
+ MCP_TOOL_TIMEOUT: 120000
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -920,15 +788,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -938,7 +806,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -955,9 +823,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -966,18 +834,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1059,9 +927,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1094,7 +962,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1122,9 +990,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1168,6 +1036,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-multi-device-docs-tester"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1183,7 +1053,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1208,9 +1078,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1222,9 +1092,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1244,9 +1114,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1262,9 +1132,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1278,6 +1148,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-multi-device-docs-tester"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-multi-device-docs-tester"
GH_AW_WORKFLOW_ID: "daily-multi-device-docs-tester"
GH_AW_WORKFLOW_NAME: "Multi-Device Docs Tester"
@@ -1301,7 +1172,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1327,9 +1198,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1360,7 +1231,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1416,8 +1287,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/daily-multi-device-docs-tester.md b/.github/workflows/daily-multi-device-docs-tester.md
index 779aacc6ef9..025711298fd 100644
--- a/.github/workflows/daily-multi-device-docs-tester.md
+++ b/.github/workflows/daily-multi-device-docs-tester.md
@@ -16,16 +16,17 @@ permissions:
tracker-id: daily-multi-device-docs-tester
engine:
id: claude
- max-turns: 30 # Prevent runaway token usage
+ max-turns: 80 # 10 devices × ~5 turns each + setup/report overhead
strict: true
timeout-minutes: 30
tools:
+ timeout: 120 # Playwright navigation on Astro dev server can take >60s; increase to 120s
playwright:
version: "v1.56.1"
bash:
- "npm install*"
- - "npm run build*"
- - "npm run preview*"
+ - "npm run dev*"
+ - "npx astro*"
- "npx playwright*"
- "curl*"
- "kill*"
@@ -70,20 +71,19 @@ You are a documentation testing specialist. Your task is to comprehensively test
## Your Mission
-Build the documentation site locally, serve it, and perform comprehensive multi-device testing. Test layout responsiveness, accessibility, interactive elements, and visual rendering across all device types. Use a single Playwright browser instance for efficiency.
+Start the documentation development server and perform comprehensive multi-device testing. Test layout responsiveness, accessibility, interactive elements, and visual rendering across all device types. Use a single Playwright browser instance for efficiency.
-## Step 1: Build and Serve
+## Step 1: Install Dependencies and Start Server
-Navigate to the docs folder and build the site:
+Navigate to the docs folder and install dependencies:
```bash
cd ${{ github.workspace }}/docs
npm install
-npm run build
```
Follow the shared **Documentation Server Lifecycle Management** instructions:
-1. Start the preview server (section "Starting the Documentation Preview Server")
+1. Start the dev server (section "Starting the Documentation Preview Server")
2. Wait for server readiness (section "Waiting for Server Readiness")
## Step 2: Device Configuration
@@ -100,25 +100,43 @@ Test these device types based on input `${{ inputs.devices }}`:
Playwright is provided through an MCP server interface, **NOT** as an npm package. You must use the MCP Playwright tools:
-- ✅ **Correct**: Use MCP tools like `mcp__playwright__browser_navigate`, `mcp__playwright__browser_run_code`, etc.
+- ✅ **Correct**: Use `mcp__playwright__browser_run_code` with `page.goto(..., { waitUntil: 'domcontentloaded' })`
- ❌ **Incorrect**: Do NOT try to `require('playwright')` or create standalone Node.js scripts
- ❌ **Incorrect**: Do NOT install playwright via npm - it's already available through MCP
+**⚠️ CRITICAL: Navigation Timeout Prevention**
+
+The Astro development server uses Vite, which loads many JavaScript modules per page. Using the default `waitUntil: 'load'` or `waitForLoadState('networkidle')` will cause 60s timeouts because the browser waits for all modules to finish. **Always use `waitUntil: 'domcontentloaded'`** for navigation:
+
+- ✅ **Correct**: `page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 })`
+- ❌ **Never use**: `page.waitForLoadState('networkidle')` — causes guaranteed timeouts
+- ❌ **Never use**: `mcp__playwright__browser_navigate` for first load — it uses default 'load' wait which times out
+
**Example Usage:**
+```bash
+# First, get the container's bridge IP (needed for Playwright - see shared lifecycle instructions)
+SERVER_IP=$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{print $7; exit}')
+if [ -z "$SERVER_IP" ]; then SERVER_IP=$(hostname -I | awk '{print $1}'); fi
+echo "Playwright server URL: http://${SERVER_IP}:4321/gh-aw/"
+```
+
```javascript
-// Use browser_run_code to execute Playwright commands
+// Use browser_run_code to execute Playwright commands.
+// IMPORTANT: Replace 172.30.0.20 below with the actual SERVER_IP from the bash command above.
+// Do NOT use "localhost" — Playwright runs with --network host so its localhost differs.
+// ALWAYS use waitUntil: 'domcontentloaded' to prevent timeout on the Vite dev server.
mcp__playwright__browser_run_code({
code: `async (page) => {
await page.setViewportSize({ width: 390, height: 844 });
- await page.goto('http://localhost:4321/gh-aw/');
+ await page.goto('http://172.30.0.20:4321/gh-aw/', { waitUntil: 'domcontentloaded', timeout: 30000 }); // substitute actual SERVER_IP
return { url: page.url(), title: await page.title() };
}`
})
```
For each device viewport, use Playwright MCP tools to:
-- Set viewport size and navigate to http://localhost:4321/gh-aw/
+- Set viewport size and navigate to `http://${SERVER_IP}:4321/gh-aw/` (substitute the bridge IP you obtained above, NOT localhost)
- Take screenshots and run accessibility audits
- Test interactions (navigation, search, buttons)
- Check for layout issues (overflow, truncation, broken layouts)
diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml
index 330d238cf7f..3ef564438f9 100644
--- a/.github/workflows/daily-news.lock.yml
+++ b/.github/workflows/daily-news.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,14 +81,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","node","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -105,9 +105,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-news.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -123,17 +123,17 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, upload_asset, missing_tool, missing_data, noop
@@ -169,6 +169,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -197,9 +198,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -224,10 +225,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -255,11 +256,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -287,10 +288,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailynews
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -312,13 +314,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
- name: Setup Python environment
@@ -412,7 +418,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -429,7 +435,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -452,16 +458,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -470,192 +476,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"daily-news\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"daily-news\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -751,6 +596,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -775,8 +621,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -787,7 +633,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -798,7 +644,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }}
run: |
@@ -817,10 +664,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e TAVILY_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e TAVILY_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -828,10 +675,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -839,6 +691,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"tavily": {
@@ -852,6 +711,13 @@ jobs:
],
"env": {
"TAVILY_API_KEY": "\${TAVILY_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -869,7 +735,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -878,7 +745,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,mcp.tavily.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,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.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,mcp.tavily.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,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.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -911,7 +778,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -949,15 +816,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,TAVILY_API_KEY'
@@ -967,7 +834,7 @@ jobs:
SECRET_TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -984,9 +851,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -995,18 +862,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1105,9 +972,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1130,7 +997,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1158,9 +1025,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1207,6 +1074,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-news"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1222,7 +1091,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1247,9 +1116,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1261,9 +1130,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1289,9 +1158,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1307,9 +1176,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1321,6 +1190,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1336,7 +1207,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1379,9 +1250,9 @@ jobs:
FILE_GLOB_FILTER: "memory/daily-news/*.json memory/daily-news/*.jsonl memory/daily-news/*.csv memory/daily-news/*.md"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1396,6 +1267,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-news"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-news-weekday"
GH_AW_WORKFLOW_ID: "daily-news"
GH_AW_WORKFLOW_NAME: "Daily News"
@@ -1417,7 +1289,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1443,9 +1315,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1462,6 +1334,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: dailynews
steps:
- name: Checkout actions folder
@@ -1474,7 +1347,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1519,7 +1392,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1575,8 +1448,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/daily-news.md b/.github/workflows/daily-news.md
index 2e836fce9bf..915aca0b617 100644
--- a/.github/workflows/daily-news.md
+++ b/.github/workflows/daily-news.md
@@ -411,17 +411,25 @@ Generate exactly **2 high-quality trend charts**:
Include the charts in your daily news discussion report with this structure:
```markdown
-## 📈 Trend Analysis
+### 📈 Trend Analysis
+
+
+Issues & Pull Requests Activity
-### Issues & Pull Requests Activity

[Brief 2-3 sentence analysis of the trends shown in this chart, highlighting notable patterns, increases, decreases, or insights]
-### Commit Activity & Contributors
+
+
+
+Commit Activity & Contributors
+

[Brief 2-3 sentence analysis of the trends shown in this chart, noting developer engagement, busy periods, or collaboration patterns]
+
+
```
### Python Implementation Notes
diff --git a/.github/workflows/daily-observability-report.lock.yml b/.github/workflows/daily-observability-report.lock.yml
index a3eafb8d47f..7f8a2f0bdf3 100644
--- a/.github/workflows/daily-observability-report.lock.yml
+++ b/.github/workflows/daily-observability-report.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,18 +81,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate CODEX_API_KEY or OPENAI_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
@@ -111,9 +111,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-observability-report.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -128,15 +128,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, close_discussion, missing_tool, missing_data, noop
@@ -170,6 +171,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -188,9 +190,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -207,10 +209,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -230,11 +232,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -261,10 +263,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyobservabilityreport
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -285,7 +288,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -321,7 +324,11 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -344,9 +351,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -356,7 +363,7 @@ jobs:
- name: Install Codex
run: npm install -g @openai/codex@latest
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -365,10 +372,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -383,200 +390,37 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"close_discussion":{"max":10},"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[observability] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Close a GitHub discussion with a resolution comment and optional reason. You can and should always add a comment when closing a discussion to explain the action or provide context. Use this to mark discussions as resolved, answered, or no longer needed. The closing comment should explain why the discussion is being closed. If the discussion is already closed, a comment will still be posted. CONSTRAINTS: Maximum 10 discussion(s) can be closed.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Closing comment explaining why the discussion is being closed and summarizing any resolution or conclusion.",
- "type": "string"
- },
- "discussion_number": {
- "description": "Discussion number to close. This is the numeric ID from the GitHub URL (e.g., 678 in github.com/owner/repo/discussions/678). If omitted, closes the discussion that triggered this workflow (requires a discussion event trigger).",
- "type": [
- "number",
- "string"
- ]
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Resolution reason: RESOLVED (issue addressed), DUPLICATE (discussed elsewhere), OUTDATED (no longer relevant), or ANSWERED (question answered).",
- "enum": [
- "RESOLVED",
- "DUPLICATE",
- "OUTDATED",
- "ANSWERED"
- ],
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "close_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "close_discussion": " CONSTRAINTS: Maximum 10 discussion(s) can be closed.",
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[observability] \". Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"close_discussion": {
"defaultMax": 1,
@@ -690,6 +534,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -714,8 +559,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -726,7 +571,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -734,7 +579,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -753,7 +599,7 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="codex"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
cat > /tmp/gh-aw/mcp-config/config.toml << GH_AW_MCP_CONFIG_EOF
[history]
@@ -768,6 +614,11 @@ jobs:
mounts = ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"]
env_vars = ["DEBUG", "GH_TOKEN", "GITHUB_TOKEN", "GITHUB_ACTOR", "GITHUB_REPOSITORY"]
+ [mcp_servers.agenticworkflows."guard-policies"]
+
+ [mcp_servers.agenticworkflows."guard-policies".write-sink]
+ accept = ["*"]
+
[mcp_servers.github]
user_agent = "daily-observability-report-for-awf-firewall-and-mcp-gateway"
startup_timeout_sec = 120
@@ -782,10 +633,15 @@ jobs:
[mcp_servers.safeoutputs.headers]
Authorization = "$GH_AW_SAFE_OUTPUTS_API_KEY"
+
+ [mcp_servers.safeoutputs."guard-policies"]
+
+ [mcp_servers.safeoutputs."guard-policies".write-sink]
+ accept = ["*"]
GH_AW_MCP_CONFIG_EOF
# Generate JSON config for MCP gateway
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -797,16 +653,28 @@ jobs:
"GITHUB_TOKEN": "$GITHUB_TOKEN",
"GITHUB_ACTOR": "$GITHUB_ACTOR",
"GITHUB_REPOSITORY": "$GITHUB_REPOSITORY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,discussions,actions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -814,6 +682,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -831,13 +706,14 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Codex
run: |
set -o pipefail
mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" && codex ${GH_AW_MODEL_AGENT_CODEX:+-c model="$GH_AW_MODEL_AGENT_CODEX" }exec -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
@@ -876,15 +752,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'CODEX_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY'
@@ -895,7 +771,7 @@ jobs:
SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -912,9 +788,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -923,18 +799,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_codex_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_codex_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1009,9 +885,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1024,7 +900,7 @@ jobs:
set -o pipefail
mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,host.docker.internal,openai.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,host.docker.internal,openai.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" && codex ${GH_AW_MODEL_DETECTION_CODEX:+-c model="$GH_AW_MODEL_DETECTION_CODEX" }exec -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
@@ -1048,9 +924,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1094,6 +970,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-observability-report"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1109,7 +987,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1134,9 +1012,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1148,9 +1026,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1172,9 +1050,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1190,9 +1068,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
pre_activation:
@@ -1213,7 +1091,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1222,9 +1100,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
safe_outputs:
@@ -1239,6 +1117,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-observability-report"
GH_AW_ENGINE_ID: "codex"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-observability-report"
GH_AW_WORKFLOW_ID: "daily-observability-report"
GH_AW_WORKFLOW_NAME: "Daily Observability Report for AWF Firewall and MCP Gateway"
@@ -1260,7 +1139,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1286,9 +1165,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-performance-summary.lock.yml b/.github/workflows/daily-performance-summary.lock.yml
index 70f12820a06..ec03e3756a9 100644
--- a/.github/workflows/daily-performance-summary.lock.yml
+++ b/.github/workflows/daily-performance-summary.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,18 +81,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -110,9 +110,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-performance-summary.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -127,16 +127,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, close_discussion, upload_asset, missing_tool, missing_data, noop
@@ -172,6 +172,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -196,9 +197,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -217,10 +218,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -242,11 +243,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -273,10 +274,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyperformancesummary
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -298,13 +300,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup Python environment
run: |
mkdir -p /tmp/gh-aw/python/{data,charts,artifacts}
@@ -330,7 +336,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -360,16 +366,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -378,219 +384,32 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"close_discussion":{"max":10},"create_discussion":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[daily performance] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Close a GitHub discussion with a resolution comment and optional reason. You can and should always add a comment when closing a discussion to explain the action or provide context. Use this to mark discussions as resolved, answered, or no longer needed. The closing comment should explain why the discussion is being closed. If the discussion is already closed, a comment will still be posted. CONSTRAINTS: Maximum 10 discussion(s) can be closed.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Closing comment explaining why the discussion is being closed and summarizing any resolution or conclusion.",
- "type": "string"
- },
- "discussion_number": {
- "description": "Discussion number to close. This is the numeric ID from the GitHub URL (e.g., 678 in github.com/owner/repo/discussions/678). If omitted, closes the discussion that triggered this workflow (requires a discussion event trigger).",
- "type": [
- "number",
- "string"
- ]
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Resolution reason: RESOLVED (issue addressed), DUPLICATE (discussed elsewhere), OUTDATED (no longer relevant), or ANSWERED (question answered).",
- "enum": [
- "RESOLVED",
- "DUPLICATE",
- "OUTDATED",
- "ANSWERED"
- ],
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "close_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "close_discussion": " CONSTRAINTS: Maximum 10 discussion(s) can be closed.",
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[daily performance] \". Discussions will be created in category \"audits\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"close_discussion": {
"defaultMax": 1,
@@ -713,6 +532,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -737,8 +557,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -749,16 +569,16 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Setup MCP Scripts Config
run: |
- mkdir -p /opt/gh-aw/mcp-scripts/logs
- cat > /opt/gh-aw/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
+ mkdir -p ${GH_AW_HOME}/mcp-scripts/logs
+ cat > ${GH_AW_HOME}/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
{
"serverName": "mcpscripts",
"version": "1.0.0",
- "logDir": "/opt/gh-aw/mcp-scripts/logs",
+ "logDir": "${GH_AW_HOME}/mcp-scripts/logs",
"tools": [
{
"name": "github-discussion-query",
@@ -849,7 +669,7 @@ jobs:
]
}
GH_AW_MCP_SCRIPTS_TOOLS_EOF
- cat > /opt/gh-aw/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
const path = require("path");
const { startHttpServer } = require("./mcp_scripts_mcp_server_http.cjs");
const configPath = path.join(__dirname, "tools.json");
@@ -858,17 +678,17 @@ jobs:
startHttpServer(configPath, {
port: port,
stateless: true,
- logDir: "/opt/gh-aw/mcp-scripts/logs"
+ logDir: process.env.GH_AW_HOME + "/mcp-scripts/logs"
}).catch(error => {
console.error("Failed to start mcp-scripts HTTP server:", error);
process.exit(1);
});
GH_AW_MCP_SCRIPTS_SERVER_EOF
- chmod +x /opt/gh-aw/mcp-scripts/mcp-server.cjs
+ chmod +x ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs
- name: Setup MCP Scripts Tool Files
run: |
- cat > /opt/gh-aw/mcp-scripts/github-discussion-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-DISCUSSION-QUERY_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/github-discussion-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-DISCUSSION-QUERY_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: github-discussion-query
# Query GitHub discussions with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter.
@@ -1004,8 +824,8 @@ jobs:
fi
GH_AW_MCP_SCRIPTS_SH_GITHUB-DISCUSSION-QUERY_EOF
- chmod +x /opt/gh-aw/mcp-scripts/github-discussion-query.sh
- cat > /opt/gh-aw/mcp-scripts/github-issue-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-ISSUE-QUERY_EOF'
+ chmod +x ${GH_AW_HOME}/mcp-scripts/github-discussion-query.sh
+ cat > ${GH_AW_HOME}/mcp-scripts/github-issue-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-ISSUE-QUERY_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: github-issue-query
# Query GitHub issues with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter.
@@ -1085,8 +905,8 @@ jobs:
GH_AW_MCP_SCRIPTS_SH_GITHUB-ISSUE-QUERY_EOF
- chmod +x /opt/gh-aw/mcp-scripts/github-issue-query.sh
- cat > /opt/gh-aw/mcp-scripts/github-pr-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-PR-QUERY_EOF'
+ chmod +x ${GH_AW_HOME}/mcp-scripts/github-issue-query.sh
+ cat > ${GH_AW_HOME}/mcp-scripts/github-pr-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-PR-QUERY_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: github-pr-query
# Query GitHub pull requests with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter.
@@ -1172,7 +992,7 @@ jobs:
GH_AW_MCP_SCRIPTS_SH_GITHUB-PR-QUERY_EOF
- chmod +x /opt/gh-aw/mcp-scripts/github-pr-query.sh
+ chmod +x ${GH_AW_HOME}/mcp-scripts/github-pr-query.sh
- name: Generate MCP Scripts Server Config
id: mcp-scripts-config
@@ -1205,7 +1025,7 @@ jobs:
export GH_AW_MCP_SCRIPTS_PORT
export GH_AW_MCP_SCRIPTS_API_KEY
- bash /opt/gh-aw/actions/start_mcp_scripts_server.sh
+ bash ${GH_AW_HOME}/actions/start_mcp_scripts_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -1219,7 +1039,8 @@ jobs:
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -1237,10 +1058,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e GH_TOKEN -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e GH_TOKEN -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -1248,10 +1069,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,discussions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"mcpscripts": {
@@ -1259,6 +1085,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_MCP_SCRIPTS_PORT",
"headers": {
"Authorization": "\${GH_AW_MCP_SCRIPTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"safeoutputs": {
@@ -1266,6 +1099,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -1283,7 +1123,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -1292,7 +1133,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,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,files.pythonhosted.org,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,static.crates.io,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,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,files.pythonhosted.org,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,static.crates.io,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1324,7 +1165,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -1362,15 +1203,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -1380,7 +1221,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1397,9 +1238,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1408,27 +1249,27 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Scripts logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_scripts_logs.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_scripts_logs.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1519,9 +1360,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1544,7 +1385,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1571,9 +1412,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1619,6 +1460,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-performance-summary"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1634,7 +1477,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1659,9 +1502,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1673,9 +1516,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1698,9 +1541,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1716,9 +1559,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1733,6 +1576,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-performance-summary"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-performance-summary"
GH_AW_WORKFLOW_ID: "daily-performance-summary"
GH_AW_WORKFLOW_NAME: "Daily Project Performance Summary Generator (Using MCP Scripts)"
@@ -1754,7 +1598,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1780,9 +1624,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1799,6 +1643,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: dailyperformancesummary
steps:
- name: Checkout actions folder
@@ -1811,7 +1656,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1856,7 +1701,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1912,8 +1757,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/daily-regulatory.lock.yml b/.github/workflows/daily-regulatory.lock.yml
index ea88c62ada4..9af6eb9b52c 100644
--- a/.github/workflows/daily-regulatory.lock.yml
+++ b/.github/workflows/daily-regulatory.lock.yml
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,18 +80,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -109,9 +109,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-regulatory.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -126,15 +126,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, close_discussion, missing_tool, missing_data, noop
@@ -168,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -189,9 +190,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -207,10 +208,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -229,11 +230,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -260,10 +261,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyregulatory
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -285,13 +287,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -314,16 +320,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -332,194 +338,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"close_discussion":{"max":10},"create_discussion":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[daily regulatory] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Close a GitHub discussion with a resolution comment and optional reason. You can and should always add a comment when closing a discussion to explain the action or provide context. Use this to mark discussions as resolved, answered, or no longer needed. The closing comment should explain why the discussion is being closed. If the discussion is already closed, a comment will still be posted. CONSTRAINTS: Maximum 10 discussion(s) can be closed.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Closing comment explaining why the discussion is being closed and summarizing any resolution or conclusion.",
- "type": "string"
- },
- "discussion_number": {
- "description": "Discussion number to close. This is the numeric ID from the GitHub URL (e.g., 678 in github.com/owner/repo/discussions/678). If omitted, closes the discussion that triggered this workflow (requires a discussion event trigger).",
- "type": [
- "number",
- "string"
- ]
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Resolution reason: RESOLVED (issue addressed), DUPLICATE (discussed elsewhere), OUTDATED (no longer relevant), or ANSWERED (question answered).",
- "enum": [
- "RESOLVED",
- "DUPLICATE",
- "OUTDATED",
- "ANSWERED"
- ],
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "close_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "close_discussion": " CONSTRAINTS: Maximum 10 discussion(s) can be closed.",
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[daily regulatory] \". Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"close_discussion": {
"defaultMax": 1,
@@ -633,6 +476,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -657,8 +501,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -669,16 +513,16 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Setup MCP Scripts Config
run: |
- mkdir -p /opt/gh-aw/mcp-scripts/logs
- cat > /opt/gh-aw/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
+ mkdir -p ${GH_AW_HOME}/mcp-scripts/logs
+ cat > ${GH_AW_HOME}/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
{
"serverName": "mcpscripts",
"version": "1.0.0",
- "logDir": "/opt/gh-aw/mcp-scripts/logs",
+ "logDir": "${GH_AW_HOME}/mcp-scripts/logs",
"tools": [
{
"name": "github-discussion-query",
@@ -769,7 +613,7 @@ jobs:
]
}
GH_AW_MCP_SCRIPTS_TOOLS_EOF
- cat > /opt/gh-aw/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
const path = require("path");
const { startHttpServer } = require("./mcp_scripts_mcp_server_http.cjs");
const configPath = path.join(__dirname, "tools.json");
@@ -778,17 +622,17 @@ jobs:
startHttpServer(configPath, {
port: port,
stateless: true,
- logDir: "/opt/gh-aw/mcp-scripts/logs"
+ logDir: process.env.GH_AW_HOME + "/mcp-scripts/logs"
}).catch(error => {
console.error("Failed to start mcp-scripts HTTP server:", error);
process.exit(1);
});
GH_AW_MCP_SCRIPTS_SERVER_EOF
- chmod +x /opt/gh-aw/mcp-scripts/mcp-server.cjs
+ chmod +x ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs
- name: Setup MCP Scripts Tool Files
run: |
- cat > /opt/gh-aw/mcp-scripts/github-discussion-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-DISCUSSION-QUERY_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/github-discussion-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-DISCUSSION-QUERY_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: github-discussion-query
# Query GitHub discussions with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter.
@@ -924,8 +768,8 @@ jobs:
fi
GH_AW_MCP_SCRIPTS_SH_GITHUB-DISCUSSION-QUERY_EOF
- chmod +x /opt/gh-aw/mcp-scripts/github-discussion-query.sh
- cat > /opt/gh-aw/mcp-scripts/github-issue-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-ISSUE-QUERY_EOF'
+ chmod +x ${GH_AW_HOME}/mcp-scripts/github-discussion-query.sh
+ cat > ${GH_AW_HOME}/mcp-scripts/github-issue-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-ISSUE-QUERY_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: github-issue-query
# Query GitHub issues with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter.
@@ -1005,8 +849,8 @@ jobs:
GH_AW_MCP_SCRIPTS_SH_GITHUB-ISSUE-QUERY_EOF
- chmod +x /opt/gh-aw/mcp-scripts/github-issue-query.sh
- cat > /opt/gh-aw/mcp-scripts/github-pr-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-PR-QUERY_EOF'
+ chmod +x ${GH_AW_HOME}/mcp-scripts/github-issue-query.sh
+ cat > ${GH_AW_HOME}/mcp-scripts/github-pr-query.sh << 'GH_AW_MCP_SCRIPTS_SH_GITHUB-PR-QUERY_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: github-pr-query
# Query GitHub pull requests with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter.
@@ -1092,7 +936,7 @@ jobs:
GH_AW_MCP_SCRIPTS_SH_GITHUB-PR-QUERY_EOF
- chmod +x /opt/gh-aw/mcp-scripts/github-pr-query.sh
+ chmod +x ${GH_AW_HOME}/mcp-scripts/github-pr-query.sh
- name: Generate MCP Scripts Server Config
id: mcp-scripts-config
@@ -1125,7 +969,7 @@ jobs:
export GH_AW_MCP_SCRIPTS_PORT
export GH_AW_MCP_SCRIPTS_API_KEY
- bash /opt/gh-aw/actions/start_mcp_scripts_server.sh
+ bash ${GH_AW_HOME}/actions/start_mcp_scripts_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -1136,7 +980,8 @@ jobs:
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -1154,10 +999,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e GH_TOKEN -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e GH_TOKEN -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -1165,10 +1010,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,discussions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"mcpscripts": {
@@ -1176,6 +1026,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_MCP_SCRIPTS_PORT",
"headers": {
"Authorization": "\${GH_AW_MCP_SCRIPTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"safeoutputs": {
@@ -1183,6 +1040,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -1200,7 +1064,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -1209,7 +1074,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1238,7 +1103,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -1276,15 +1141,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -1294,7 +1159,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1311,9 +1176,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1322,27 +1187,27 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Scripts logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_scripts_logs.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_scripts_logs.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1418,9 +1283,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1443,7 +1308,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1470,9 +1335,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1516,6 +1381,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-regulatory"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1531,7 +1398,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1556,9 +1423,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1570,9 +1437,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1595,9 +1462,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1613,9 +1480,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1630,6 +1497,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-regulatory"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-regulatory"
GH_AW_WORKFLOW_ID: "daily-regulatory"
GH_AW_WORKFLOW_NAME: "Daily Regulatory Report Generator"
@@ -1651,7 +1519,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1677,9 +1545,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-regulatory.md b/.github/workflows/daily-regulatory.md
index f1f3055de35..95a08937b88 100644
--- a/.github/workflows/daily-regulatory.md
+++ b/.github/workflows/daily-regulatory.md
@@ -275,7 +275,7 @@ Brief 2-3 paragraph executive summary highlighting:
📋 Full Regulatory Report
-## 📊 Reports Reviewed
+### 📊 Reports Reviewed
| Report | Title | Created | Status |
|--------|-------|---------|--------|
@@ -283,7 +283,7 @@ Brief 2-3 paragraph executive summary highlighting:
| [Report 2] | [Title] | [Timestamp] | ✅ Valid / ⚠️ Issues / ❌ Failed |
| ... | ... | ... | ... |
-## 🔍 Data Consistency Analysis
+### 🔍 Data Consistency Analysis
### Cross-Report Metrics Comparison
@@ -307,7 +307,7 @@ Reference scratchpad/metrics-glossary.md for metric definitions and scopes.
- **Critical Discrepancies**: [COUNT]
- **Minor Discrepancies**: [COUNT]
-## ⚠️ Issues and Anomalies
+### ⚠️ Issues and Anomalies
### Critical Issues
@@ -333,7 +333,7 @@ Reference scratchpad/metrics-glossary.md for metric definitions and scopes.
- [Note about incomplete reports]
- [Note about data freshness]
-## 📈 Trend Analysis
+### 📈 Trend Analysis
### Week-over-Week Comparison
@@ -348,7 +348,7 @@ Reference scratchpad/metrics-glossary.md for metric definitions and scopes.
- [Pattern identified across reports]
- [Concerning or positive trend]
-## 📝 Per-Report Analysis
+### 📝 Per-Report Analysis
### [Report 1 Name]
@@ -367,7 +367,7 @@ Reference scratchpad/metrics-glossary.md for metric definitions and scopes.
[Same structure as above]
-## 💡 Recommendations
+### 💡 Recommendations
### Process Improvements
@@ -383,7 +383,7 @@ Reference scratchpad/metrics-glossary.md for metric definitions and scopes.
1. **[Suggestion]**: [For improving consistency across reports]
-## 📊 Regulatory Metrics
+### 📊 Regulatory Metrics
| Metric | Value |
|--------|-------|
diff --git a/.github/workflows/daily-rendering-scripts-verifier.lock.yml b/.github/workflows/daily-rendering-scripts-verifier.lock.yml
index dcf483d3b6f..2643816cb2b 100644
--- a/.github/workflows/daily-rendering-scripts-verifier.lock.yml
+++ b/.github/workflows/daily-rendering-scripts-verifier.lock.yml
@@ -28,7 +28,7 @@
# - shared/activation-app.md
# - shared/reporting.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"a7011f09224b79475d25a6867214db655a751d141f5c99b9344f89befa74976a","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"fe7e183fb438b78cdfe76d148d53c40c421dcac32b7364799f1cc4d040b2f552","strict":true}
name: "Daily Rendering Scripts Verifier"
"on":
@@ -68,7 +68,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -83,18 +83,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -112,9 +112,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-rendering-scripts-verifier.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -129,21 +129,22 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -175,6 +176,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -196,9 +198,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -218,10 +220,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -244,11 +246,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -274,10 +276,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyrenderingscriptsverifier
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -298,7 +301,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -334,10 +337,14 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -367,9 +374,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -377,7 +384,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -388,10 +395,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -406,173 +413,36 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request":{"expires":72,"max":1,"reviewers":["copilot"],"title_prefix":"[rendering-scripts] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[rendering-scripts] \". Labels [\"rendering\" \"javascript\" \"automated-fix\"] will be automatically added. Reviewers [\"copilot\"] will be assigned.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[rendering-scripts] \". Labels [\"rendering\" \"javascript\" \"automated-fix\"] will be automatically added. Reviewers [\"copilot\"] will be assigned."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -669,6 +539,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -693,8 +564,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -705,7 +576,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -713,7 +584,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -732,9 +604,9 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -746,16 +618,28 @@ jobs:
"GITHUB_TOKEN": "$GITHUB_TOKEN",
"GITHUB_ACTOR": "$GITHUB_ACTOR",
"GITHUB_REPOSITORY": "$GITHUB_REPOSITORY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -763,6 +647,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -780,7 +671,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -890,7 +782,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(cat),Bash(cat*),Bash(cd *),Bash(date),Bash(echo),Bash(echo*),Bash(find*),Bash(git add:*),Bash(git branch:*),Bash(git checkout:*),Bash(git commit:*),Bash(git merge:*),Bash(git rm:*),Bash(git status),Bash(git switch:*),Bash(grep),Bash(head),Bash(head*),Bash(jq*),Bash(ls),Bash(ls*),Bash(node *),Bash(npm*),Bash(pwd),Bash(sort),Bash(tail),Bash(tail*),Bash(uniq),Bash(wc),Bash(wc*),Bash(yq),BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -934,15 +826,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -952,7 +844,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -969,9 +861,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -980,18 +872,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1071,9 +963,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1106,7 +998,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1134,9 +1026,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1181,6 +1073,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-rendering-scripts-verifier"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1196,7 +1090,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1221,9 +1115,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1235,9 +1129,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1259,9 +1153,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1277,9 +1171,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1292,9 +1186,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
pre_activation:
@@ -1315,7 +1209,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1324,10 +1218,19 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
+ - name: Generate GitHub App token for skip-if checks
+ id: pre-activation-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
- name: Check skip-if-match query
id: check_skip_if_match
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1336,10 +1239,11 @@ jobs:
GH_AW_WORKFLOW_NAME: "Daily Rendering Scripts Verifier"
GH_AW_SKIP_MAX_MATCHES: "1"
with:
+ github-token: ${{ steps.pre-activation-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_skip_if_match.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_skip_if_match.cjs');
await main();
safe_outputs:
@@ -1356,6 +1260,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-rendering-scripts-verifier"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-rendering-scripts-verifier"
GH_AW_WORKFLOW_ID: "daily-rendering-scripts-verifier"
GH_AW_WORKFLOW_NAME: "Daily Rendering Scripts Verifier"
@@ -1379,7 +1284,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1434,9 +1339,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1453,6 +1358,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: dailyrenderingscriptsverifier
steps:
- name: Checkout actions folder
@@ -1465,7 +1371,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/daily-repo-chronicle.lock.yml b/.github/workflows/daily-repo-chronicle.lock.yml
index 63e596d431a..2c38080f693 100644
--- a/.github/workflows/daily-repo-chronicle.lock.yml
+++ b/.github/workflows/daily-repo-chronicle.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,14 +79,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","node","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -103,9 +103,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-repo-chronicle.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -120,16 +120,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, upload_asset, missing_tool, missing_data, noop
@@ -165,6 +165,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -188,9 +189,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -209,10 +210,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -234,11 +235,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -265,10 +266,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyrepochronicle
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -290,13 +292,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup Python environment
run: "# Create working directory for Python scripts\nmkdir -p /tmp/gh-aw/python\nmkdir -p /tmp/gh-aw/python/data\nmkdir -p /tmp/gh-aw/python/charts\nmkdir -p /tmp/gh-aw/python/artifacts\n\necho \"Python environment setup complete\"\necho \"Working directory: /tmp/gh-aw/python\"\necho \"Data directory: /tmp/gh-aw/python/data\"\necho \"Charts directory: /tmp/gh-aw/python/charts\"\necho \"Artifacts directory: /tmp/gh-aw/python/artifacts\"\n"
- name: Install Python scientific libraries
@@ -322,7 +328,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -352,16 +358,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -370,177 +376,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"📰 \". Discussions will be created in category \"announcements\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"📰 \". Discussions will be created in category \"announcements\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -636,6 +496,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -660,8 +521,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -672,7 +533,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -683,7 +544,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -701,10 +563,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -712,10 +574,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,discussions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -723,6 +590,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -740,7 +614,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -749,7 +624,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,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.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,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.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -781,7 +656,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -819,15 +694,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -836,7 +711,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -853,9 +728,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -864,18 +739,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -965,9 +840,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -990,7 +865,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1018,9 +893,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1066,6 +941,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-repo-chronicle"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1081,7 +958,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1106,9 +983,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1120,9 +997,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1144,9 +1021,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1162,9 +1039,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1179,6 +1056,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-repo-chronicle"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-repo-chronicle"
GH_AW_WORKFLOW_ID: "daily-repo-chronicle"
GH_AW_WORKFLOW_NAME: "The Daily Repository Chronicle"
@@ -1200,7 +1078,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1226,9 +1104,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1245,6 +1123,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: dailyrepochronicle
steps:
- name: Checkout actions folder
@@ -1257,7 +1136,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1302,7 +1181,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1358,8 +1237,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/daily-repo-chronicle.md b/.github/workflows/daily-repo-chronicle.md
index d8ba805e932..436808f636d 100644
--- a/.github/workflows/daily-repo-chronicle.md
+++ b/.github/workflows/daily-repo-chronicle.md
@@ -123,7 +123,7 @@ Generate exactly **2 high-quality trend charts**:
Include the charts in your newspaper-style report with this structure:
```markdown
-## 📈 THE NUMBERS - Visualized
+### 📈 THE NUMBERS - Visualized
### Issues & Pull Requests Activity

diff --git a/.github/workflows/daily-safe-output-optimizer.lock.yml b/.github/workflows/daily-safe-output-optimizer.lock.yml
index 393c8b4b68b..51009df58a3 100644
--- a/.github/workflows/daily-safe-output-optimizer.lock.yml
+++ b/.github/workflows/daily-safe-output-optimizer.lock.yml
@@ -29,7 +29,7 @@
# - shared/jqschema.md
# - shared/reporting.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"4dfd985f4be0de9f2d4f273608bccc24442362bc90d685918b77fe649be45793","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"668f37207cd7196173ee0d7feb37527d5d91f4ffeda97f50c6ba554dc8226695","strict":true}
name: "Daily Safe Output Tool Optimizer"
"on":
@@ -69,7 +69,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -84,18 +84,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -113,9 +113,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-safe-output-optimizer.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -130,16 +130,17 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -173,6 +174,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -196,9 +198,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -218,10 +220,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -244,11 +246,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -274,10 +276,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailysafeoutputoptimizer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -298,7 +301,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -334,7 +337,11 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
- env:
@@ -344,7 +351,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -374,9 +381,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -384,7 +391,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -395,10 +402,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -413,173 +420,36 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":48,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[safeoutputs] \". Labels [\"bug\" \"safe-outputs\" \"tool-improvement\" \"automated-analysis\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[safeoutputs] \". Labels [\"bug\" \"safe-outputs\" \"tool-improvement\" \"automated-analysis\" \"cookie\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -673,6 +543,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -697,8 +568,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -709,7 +580,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -717,7 +588,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -736,9 +608,9 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -750,16 +622,28 @@ jobs:
"GITHUB_TOKEN": "$GITHUB_TOKEN",
"GITHUB_ACTOR": "$GITHUB_ACTOR",
"GITHUB_REPOSITORY": "$GITHUB_REPOSITORY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -767,6 +651,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -784,7 +675,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -878,7 +770,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(/tmp/gh-aw/jqschema.sh),Bash(cat),Bash(date),Bash(echo),Bash(git),Bash(grep),Bash(head),Bash(jq *),Bash(ls),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -923,15 +815,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -941,7 +833,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -958,9 +850,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -969,18 +861,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1059,9 +951,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1094,7 +986,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1122,9 +1014,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1168,6 +1060,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-safe-output-optimizer"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1183,7 +1077,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1207,9 +1101,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1220,9 +1114,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1241,9 +1135,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1258,9 +1152,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
pre_activation:
@@ -1281,7 +1175,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1290,10 +1184,19 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
+ - name: Generate GitHub App token for skip-if checks
+ id: pre-activation-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
- name: Check skip-if-match query
id: check_skip_if_match
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1302,10 +1205,11 @@ jobs:
GH_AW_WORKFLOW_NAME: "Daily Safe Output Tool Optimizer"
GH_AW_SKIP_MAX_MATCHES: "1"
with:
+ github-token: ${{ steps.pre-activation-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_skip_if_match.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_skip_if_match.cjs');
await main();
safe_outputs:
@@ -1319,6 +1223,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-safe-output-optimizer"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "daily-safe-output-optimizer"
GH_AW_WORKFLOW_NAME: "Daily Safe Output Tool Optimizer"
outputs:
@@ -1341,7 +1246,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1367,9 +1272,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1386,6 +1291,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: dailysafeoutputoptimizer
steps:
- name: Checkout actions folder
@@ -1398,7 +1304,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/daily-safe-output-optimizer.md b/.github/workflows/daily-safe-output-optimizer.md
index f25f9963c82..2055bae233d 100644
--- a/.github/workflows/daily-safe-output-optimizer.md
+++ b/.github/workflows/daily-safe-output-optimizer.md
@@ -231,11 +231,14 @@ Use the cache memory folder `/tmp/gh-aw/cache-memory/` to build persistent knowl
```markdown
# Improve [Tool Name] Description to Prevent Agent Errors
-## Summary
+### Summary
Analysis of the last 24 hours of workflow runs identified **[N] errors** where agents incorrectly used the `[tool_name]` safe output tool. The workflow prompts appear correct, indicating the tool description needs improvement.
-## Error Analysis
+
+🔍 Error Analysis Details
+
+### Error Analysis
### Error Pattern 1: [Description]
@@ -269,7 +272,9 @@ Analysis of the last 24 hours of workflow runs identified **[N] errors** where a
[Repeat structure above for additional patterns]
-## Current Tool Description
+
+
+### Current Tool Description
Current description from safe_outputs_tools.json
@@ -280,16 +285,16 @@ Analysis of the last 24 hours of workflow runs identified **[N] errors** where a
-## Root Cause Analysis
+### Root Cause Analysis
The tool description issues:
1. [Specific problem 1 - e.g., "Field description is ambiguous"]
2. [Specific problem 2 - e.g., "Required fields not clearly marked"]
3. [Specific problem 3 - e.g., "Similar field names cause confusion"]
-## Recommended Improvements
+### Recommended Improvements
-### Update Tool Description
+#### Update Tool Description
Modify the description in `pkg/workflow/js/safe_outputs_tools.json`:
@@ -307,13 +312,13 @@ Modify the description in `pkg/workflow/js/safe_outputs_tools.json`:
- Make it clearer that `[field_name]` is required
- Add note about what happens if omitted
-### Update Field Descriptions
+#### Update Field Descriptions
For inputSchema properties:
- **`[field_1]`**: [Current description] → [Improved description]
- **`[field_2]`**: [Current description] → [Improved description]
-## Affected Workflows
+### Affected Workflows
The following workflows had errors with this tool:
@@ -321,7 +326,7 @@ The following workflows had errors with this tool:
- `[workflow-2]` - [N] errors
- `[workflow-3]` - [N] errors
-## Testing Plan
+### Testing Plan
After updating the tool description:
@@ -330,7 +335,7 @@ After updating the tool description:
3. Monitor logs for 2-3 days to verify error rate decreases
4. Check if agents correctly use the updated descriptions
-## Implementation Checklist
+### Implementation Checklist
- [ ] Update tool description in `pkg/workflow/js/safe_outputs_tools.json`
- [ ] Update field descriptions in inputSchema
@@ -340,7 +345,7 @@ After updating the tool description:
- [ ] Run `make test` to ensure no regressions
- [ ] Deploy and monitor error rates
-## References
+### References
- Tool schema: `pkg/workflow/js/safe_outputs_tools.json`
- MCP server loader: `actions/setup/js/safe_outputs_tools_loader.cjs`
diff --git a/.github/workflows/daily-safe-outputs-conformance.lock.yml b/.github/workflows/daily-safe-outputs-conformance.lock.yml
index 23eb1b9a6c8..fb9f6f84772 100644
--- a/.github/workflows/daily-safe-outputs-conformance.lock.yml
+++ b/.github/workflows/daily-safe-outputs-conformance.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-safe-outputs-conformance.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,15 +125,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -167,6 +167,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -185,9 +186,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -203,10 +204,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -225,11 +226,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -253,10 +254,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailysafeoutputsconformance
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -277,13 +279,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -306,9 +312,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -316,7 +322,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -327,167 +333,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":24,"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 10 issue(s) can be created. Title will be prefixed with \"[Safe Outputs Conformance] \". Labels [\"safe-outputs\" \"conformance\" \"automated\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 10 issue(s) can be created. Title will be prefixed with \"[Safe Outputs Conformance] \". Labels [\"safe-outputs\" \"conformance\" \"automated\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -581,6 +450,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -605,8 +475,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -617,7 +487,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -625,7 +495,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -643,19 +514,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "repos,issues"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -663,6 +539,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -680,7 +563,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -756,7 +640,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools Bash,BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -800,15 +684,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -818,7 +702,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -835,9 +719,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -846,18 +730,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -930,9 +814,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -965,7 +849,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -993,9 +877,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1038,6 +922,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-safe-outputs-conformance"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1053,7 +939,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1078,9 +964,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1092,9 +978,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1114,9 +1000,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1132,9 +1018,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1148,6 +1034,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-safe-outputs-conformance"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "safe-outputs-conformance"
GH_AW_WORKFLOW_ID: "daily-safe-outputs-conformance"
GH_AW_WORKFLOW_NAME: "Daily Safe Outputs Conformance Checker"
@@ -1171,7 +1058,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1197,9 +1084,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-safe-outputs-conformance.md b/.github/workflows/daily-safe-outputs-conformance.md
index 0b28e53acb2..634ae69e68e 100644
--- a/.github/workflows/daily-safe-outputs-conformance.md
+++ b/.github/workflows/daily-safe-outputs-conformance.md
@@ -116,6 +116,9 @@ Example: `SEC-001: Agent job in workflow X has write permissions`
- **Workflows**: [List workflows if applicable]
- **Handlers**: [List handler files if applicable]
+
+🔍 Current vs Expected Behavior
+
### Current Behavior
[Describe what the code currently does that violates conformance]
@@ -124,6 +127,8 @@ Example: `SEC-001: Agent job in workflow X has write permissions`
[Describe what the specification requires]
+
+
### Remediation Steps
This task can be assigned to a Copilot coding agent with the following steps:
@@ -174,11 +179,14 @@ If multiple similar issues are found (e.g., 3 handlers missing the same validati
After processing all issues, provide a summary in the workflow output:
```markdown
-## Safe Outputs Conformance Summary
+### Safe Outputs Conformance Summary
**Run Date**: $(date +%Y-%m-%d)
**Script Exit Code**: [exit_code]
+
+📊 Detailed Results & Actions
+
### Results
- **Critical Failures**: [count]
@@ -192,6 +200,8 @@ After processing all issues, provide a summary in the workflow output:
- Issues will auto-expire in 1 day if not addressed
- Older conformance issues automatically closed
+
+
### Status
[PASS/FAIL/WARN] - [Brief explanation]
diff --git a/.github/workflows/daily-secrets-analysis.lock.yml b/.github/workflows/daily-secrets-analysis.lock.yml
index f669f909879..46c9bb574e7 100644
--- a/.github/workflows/daily-secrets-analysis.lock.yml
+++ b/.github/workflows/daily-secrets-analysis.lock.yml
@@ -63,7 +63,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -78,14 +78,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -102,9 +102,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-secrets-analysis.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -119,15 +119,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, close_discussion, missing_tool, missing_data, noop
@@ -161,6 +161,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -179,9 +180,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -197,10 +198,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -219,11 +220,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -250,10 +251,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailysecretsanalysis
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -275,13 +277,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -304,16 +310,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -322,194 +328,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"close_discussion":{"max":10},"create_discussion":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[daily secrets] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Close a GitHub discussion with a resolution comment and optional reason. You can and should always add a comment when closing a discussion to explain the action or provide context. Use this to mark discussions as resolved, answered, or no longer needed. The closing comment should explain why the discussion is being closed. If the discussion is already closed, a comment will still be posted. CONSTRAINTS: Maximum 10 discussion(s) can be closed.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Closing comment explaining why the discussion is being closed and summarizing any resolution or conclusion.",
- "type": "string"
- },
- "discussion_number": {
- "description": "Discussion number to close. This is the numeric ID from the GitHub URL (e.g., 678 in github.com/owner/repo/discussions/678). If omitted, closes the discussion that triggered this workflow (requires a discussion event trigger).",
- "type": [
- "number",
- "string"
- ]
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Resolution reason: RESOLVED (issue addressed), DUPLICATE (discussed elsewhere), OUTDATED (no longer relevant), or ANSWERED (question answered).",
- "enum": [
- "RESOLVED",
- "DUPLICATE",
- "OUTDATED",
- "ANSWERED"
- ],
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "close_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "close_discussion": " CONSTRAINTS: Maximum 10 discussion(s) can be closed.",
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[daily secrets] \". Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"close_discussion": {
"defaultMax": 1,
@@ -623,6 +466,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -647,8 +491,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -659,7 +503,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -667,7 +511,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -685,10 +530,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -696,10 +541,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,discussions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -707,6 +557,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -724,7 +581,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -733,7 +591,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -762,7 +620,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -800,15 +658,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -817,7 +675,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -834,9 +692,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -845,18 +703,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -931,9 +789,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -956,7 +814,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -984,9 +842,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1030,6 +888,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-secrets-analysis"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1045,7 +905,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1070,9 +930,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1084,9 +944,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1108,9 +968,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1126,9 +986,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1143,6 +1003,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-secrets-analysis"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-secrets-analysis"
GH_AW_WORKFLOW_ID: "daily-secrets-analysis"
GH_AW_WORKFLOW_NAME: "Daily Secrets Analysis Agent"
@@ -1164,7 +1025,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1190,9 +1051,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-security-red-team.lock.yml b/.github/workflows/daily-security-red-team.lock.yml
index 165efe57a4b..57d360ee3fa 100644
--- a/.github/workflows/daily-security-red-team.lock.yml
+++ b/.github/workflows/daily-security-red-team.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-security-red-team.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -126,15 +126,15 @@ jobs:
GH_AW_GITHUB_SERVER_URL: ${{ github.server_url }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -168,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -187,9 +188,9 @@ jobs:
GH_AW_GITHUB_SERVER_URL: ${{ github.server_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -206,10 +207,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -229,11 +230,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -257,10 +258,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailysecurityredteam
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -281,13 +283,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -310,9 +316,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -320,7 +326,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -331,167 +337,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"max":5},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 5 issue(s) can be created. Title will be prefixed with \"🚨 [SECURITY]\". Labels [\"security\" \"red-team\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 5 issue(s) can be created. Title will be prefixed with \"🚨 [SECURITY]\". Labels [\"security\" \"red-team\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -585,6 +454,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -609,8 +479,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -621,7 +491,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -629,7 +499,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -647,19 +518,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "repos,issues"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -667,6 +543,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -684,7 +567,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -760,7 +644,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools Bash,BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -804,15 +688,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -822,7 +706,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -839,9 +723,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -850,18 +734,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -934,9 +818,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -969,7 +853,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -997,9 +881,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1042,6 +926,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-security-red-team"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1057,7 +943,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1082,9 +968,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1096,9 +982,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1118,9 +1004,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1136,9 +1022,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1152,6 +1038,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-security-red-team"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "security-red-team"
GH_AW_WORKFLOW_ID: "daily-security-red-team"
GH_AW_WORKFLOW_NAME: "Daily Security Red Team Agent"
@@ -1175,7 +1062,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1201,9 +1088,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-semgrep-scan.lock.yml b/.github/workflows/daily-semgrep-scan.lock.yml
index b9d4f3b0a09..d9f9cef8aca 100644
--- a/.github/workflows/daily-semgrep-scan.lock.yml
+++ b/.github/workflows/daily-semgrep-scan.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-semgrep-scan.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,15 +125,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_code_scanning_alert, missing_tool, missing_data, noop
@@ -167,6 +167,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -183,9 +184,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -201,10 +202,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -223,11 +224,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -253,10 +254,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailysemgrepscan
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -278,13 +280,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -307,16 +313,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -325,178 +331,28 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine semgrep/semgrep:latest
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine semgrep/semgrep:latest
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_code_scanning_alert":{"max":0},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a code scanning alert for security vulnerabilities, code quality issues, or other findings. Alerts appear in the repository's Security tab and integrate with GitHub's security features. Use this for automated security analysis results.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "column": {
- "description": "Column number for more precise location of the issue within the line.",
- "type": [
- "number",
- "string"
- ]
- },
- "file": {
- "description": "File path relative to the repository root where the issue was found (e.g., 'src/auth/password.js').",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "line": {
- "description": "Line number where the issue was found in the file.",
- "type": [
- "number",
- "string"
- ]
- },
- "message": {
- "description": "Clear description of the security issue or finding. Include what's wrong and ideally how to fix it.",
- "type": "string"
- },
- "ruleIdSuffix": {
- "description": "Suffix to append to the rule ID for categorizing different types of findings (e.g., 'sql-injection', 'xss').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "severity": {
- "description": "Alert severity level: 'error' (critical security issues), 'warning' (potential problems), 'info' (informational), or 'note' (minor observations).",
- "enum": [
- "error",
- "warning",
- "info",
- "note"
- ],
- "type": "string"
- }
- },
- "required": [
- "file",
- "line",
- "severity",
- "message"
- ],
- "type": "object"
- },
- "name": "create_code_scanning_alert"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {},
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_code_scanning_alert": {
"defaultMax": 40,
@@ -598,6 +454,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -622,8 +479,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -634,7 +491,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -642,7 +499,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -660,10 +518,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -671,10 +529,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -682,6 +545,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"semgrep": {
@@ -695,7 +565,14 @@ jobs:
],
"tools": [
"*"
- ]
+ ],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -712,7 +589,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -721,7 +599,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -749,7 +627,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -787,15 +665,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -805,7 +683,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -822,9 +700,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -833,18 +711,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -919,9 +797,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -944,7 +822,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -971,9 +849,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1016,6 +894,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-semgrep-scan"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1031,7 +911,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1055,9 +935,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1068,9 +948,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1090,9 +970,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1107,9 +987,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1123,6 +1003,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-semgrep-scan"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "daily-semgrep-scan"
GH_AW_WORKFLOW_NAME: "Daily Semgrep Scan"
outputs:
@@ -1143,7 +1024,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1169,9 +1050,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-syntax-error-quality.lock.yml b/.github/workflows/daily-syntax-error-quality.lock.yml
index fd9f219436c..9dec18f8096 100644
--- a/.github/workflows/daily-syntax-error-quality.lock.yml
+++ b/.github/workflows/daily-syntax-error-quality.lock.yml
@@ -27,7 +27,7 @@
# Imports:
# - shared/reporting.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"c4b7c52a6c58b8c054b75b4e9240efd1b9435789fa1f35652462876451aed8b2","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"617798aaf755b934777aa3c92ed8e6c6c45d1f3b23a20ab7e5b0ff2b8536f2cf","strict":true}
name: "Daily Syntax Error Quality Check"
"on":
@@ -63,7 +63,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -78,14 +78,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -102,9 +102,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-syntax-error-quality.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -119,15 +119,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -161,6 +161,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -179,9 +180,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -197,10 +198,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -219,11 +220,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -249,10 +250,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailysyntaxerrorquality
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -274,13 +276,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup Go
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
@@ -316,175 +322,48 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[syntax-error-quality] \". Labels [\"dx\" \"error-messages\" \"automated-analysis\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[syntax-error-quality] \". Labels [\"dx\" \"error-messages\" \"automated-analysis\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -578,6 +457,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -602,8 +482,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -614,7 +494,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -622,6 +502,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -639,10 +521,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -653,6 +535,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -660,6 +548,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -677,7 +572,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -707,7 +603,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(./gh-aw compile)'\'' --allow-tool '\''shell(cat .github/workflows/*.md)'\'' --allow-tool '\''shell(cat /tmp/*.md)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(cp .github/workflows/*.md /tmp/*.md)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find .github/workflows -name '\''\'\'''\''*.md'\''\'\'''\'' -type f ! -name '\''\'\'''\''daily-*.md'\''\'\'''\'' ! -name '\''\'\'''\''*-test.md'\''\'\'''\'')'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head -n * .github/workflows/*.md)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -736,7 +632,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -774,15 +670,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -791,7 +687,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -808,9 +704,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -819,18 +715,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -905,9 +801,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -930,7 +826,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -958,9 +854,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1003,6 +899,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-syntax-error-quality"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1018,7 +916,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1043,9 +941,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1057,9 +955,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1079,9 +977,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1097,9 +995,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1113,6 +1011,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-syntax-error-quality"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-syntax-error-quality"
GH_AW_WORKFLOW_ID: "daily-syntax-error-quality"
GH_AW_WORKFLOW_NAME: "Daily Syntax Error Quality Check"
@@ -1136,7 +1035,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1162,9 +1061,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-syntax-error-quality.md b/.github/workflows/daily-syntax-error-quality.md
index 6e5cacd114f..5f88458dd24 100644
--- a/.github/workflows/daily-syntax-error-quality.md
+++ b/.github/workflows/daily-syntax-error-quality.md
@@ -12,7 +12,6 @@ tracker-id: daily-syntax-error-quality
engine: copilot
tools:
github:
- lockdown: false
toolsets:
- default
bash:
diff --git a/.github/workflows/daily-team-evolution-insights.lock.yml b/.github/workflows/daily-team-evolution-insights.lock.yml
index 1036437fb23..8055ed0910a 100644
--- a/.github/workflows/daily-team-evolution-insights.lock.yml
+++ b/.github/workflows/daily-team-evolution-insights.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["github.com","api.github.com","anthropic.com","api.anthropic.com"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "false"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-team-evolution-insights.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,15 +125,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -167,6 +167,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -185,9 +186,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -203,10 +204,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -225,11 +226,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -256,10 +257,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyteamevolutioninsights
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -280,13 +282,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -309,9 +315,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -319,7 +325,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -330,152 +336,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -562,6 +446,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -586,8 +471,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -598,7 +483,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -606,7 +491,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -624,19 +510,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "repos,issues,pull_requests,discussions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -644,6 +535,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -661,7 +559,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -737,7 +636,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools Bash,BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -781,15 +680,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -799,7 +698,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -816,9 +715,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -827,18 +726,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -911,9 +810,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -946,7 +845,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -974,9 +873,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1020,6 +919,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-team-evolution-insights"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1035,7 +936,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1060,9 +961,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1074,9 +975,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1098,9 +999,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1116,9 +1017,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1133,6 +1034,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-team-evolution-insights"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-team-evolution-insights"
GH_AW_WORKFLOW_ID: "daily-team-evolution-insights"
GH_AW_WORKFLOW_NAME: "Daily Team Evolution Insights"
@@ -1154,7 +1056,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1180,9 +1082,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-team-status.lock.yml b/.github/workflows/daily-team-status.lock.yml
index e060bc7f1d6..908cdfae52c 100644
--- a/.github/workflows/daily-team-status.lock.yml
+++ b/.github/workflows/daily-team-status.lock.yml
@@ -73,7 +73,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -88,18 +88,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -117,9 +117,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-team-status.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -134,15 +134,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -176,6 +176,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -192,9 +193,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -211,10 +212,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -234,11 +235,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -263,10 +264,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyteamstatus
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -288,13 +290,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -317,16 +323,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -335,167 +341,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[team-status] \". Labels [\"automation\" \"daily-report\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[team-status] \". Labels [\"automation\" \"daily-report\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -589,6 +458,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -613,8 +483,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -625,7 +495,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -633,7 +503,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -651,10 +522,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -662,10 +533,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -673,6 +549,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -690,7 +573,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -699,7 +583,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -727,7 +611,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -765,15 +649,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -783,7 +667,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -800,9 +684,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -811,18 +695,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -897,9 +781,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -922,7 +806,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -949,9 +833,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -994,6 +878,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-team-status"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1009,7 +895,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1036,9 +922,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1052,9 +938,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1077,9 +963,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1097,9 +983,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
pre_activation:
@@ -1120,7 +1006,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check stop-time limit
id: check_stop_time
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1129,9 +1015,9 @@ jobs:
GH_AW_WORKFLOW_NAME: "Daily Team Status"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_stop_time.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_stop_time.cjs');
await main();
safe_outputs:
@@ -1145,6 +1031,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-team-status"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-team-status"
GH_AW_WORKFLOW_ID: "daily-team-status"
GH_AW_WORKFLOW_NAME: "Daily Team Status"
@@ -1170,7 +1057,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1196,9 +1083,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-testify-uber-super-expert.lock.yml b/.github/workflows/daily-testify-uber-super-expert.lock.yml
index 76908d28e5d..8b9d6f83adc 100644
--- a/.github/workflows/daily-testify-uber-super-expert.lock.yml
+++ b/.github/workflows/daily-testify-uber-super-expert.lock.yml
@@ -30,7 +30,7 @@
# - shared/reporting.md
# - shared/safe-output-app.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"395963d5f4f9f6fd0b3bcfde48ac88bd0e446e310cb08788e1a59c20bf43ff44","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"ada3e80ee0dc8525b0cc207d302715a83eb90cb126050b90a62bf52570876f45","strict":true}
name: "Daily Testify Uber Super Expert"
"on":
@@ -69,7 +69,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -84,14 +84,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-testify-uber-super-expert.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -126,16 +126,16 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -169,6 +169,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -196,9 +197,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -221,10 +222,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -250,11 +251,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -280,10 +281,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailytestifyubersuperexpert
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -305,13 +307,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Repo memory git-based storage configuration from frontmatter processed below
- name: Clone repo-memory branch (default)
env:
@@ -321,7 +327,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -344,16 +350,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -362,182 +368,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":48,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":51200,"max_patch_size":10240}]}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[testify-expert] \". Labels [\"testing\" \"code-quality\" \"automated-analysis\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[testify-expert] \". Labels [\"testing\" \"code-quality\" \"automated-analysis\" \"cookie\"] will be automatically added."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -631,6 +485,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -655,8 +510,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -667,7 +522,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -675,7 +530,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -693,10 +549,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -704,10 +560,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -715,6 +576,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -723,7 +591,14 @@ jobs:
"args": ["--network", "host"],
"entrypoint": "serena",
"entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -740,7 +615,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -770,7 +646,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool serena --allow-tool '\''shell(cat **/*_test.go)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find . -name '\''\'\'''\''*_test.go'\''\'\'''\'' -type f)'\'' --allow-tool '\''shell(go test -v ./...)'\'' --allow-tool '\''shell(grep -r '\''\'\'''\''func Test'\''\'\'''\'' . --include='\''\'\'''\''*_test.go'\''\'\'''\'')'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc -l **/*_test.go)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -799,7 +675,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -837,15 +713,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -854,7 +730,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -871,9 +747,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -882,18 +758,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -977,9 +853,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1002,7 +878,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1030,9 +906,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1076,6 +952,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-testify-uber-super-expert"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1091,7 +969,18 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
+ - name: Generate GitHub App token
+ id: safe-outputs-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
+ permission-contents: read
+ permission-issues: write
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1114,11 +1003,11 @@ jobs:
GH_AW_WORKFLOW_NAME: "Daily Testify Uber Super Expert"
GH_AW_TRACKER_ID: "daily-testify-uber-super-expert"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1128,11 +1017,11 @@ jobs:
GH_AW_WORKFLOW_NAME: "Daily Testify Uber Super Expert"
GH_AW_TRACKER_ID: "daily-testify-uber-super-expert"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1154,11 +1043,11 @@ jobs:
GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
GH_AW_TIMEOUT_MINUTES: "20"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1172,12 +1061,25 @@ jobs:
GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }}
GH_AW_NOOP_REPORT_AS_ISSUE: "true"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
+ - name: Invalidate GitHub App token
+ if: always() && steps.safe-outputs-app-token.outputs.token != ''
+ env:
+ TOKEN: ${{ steps.safe-outputs-app-token.outputs.token }}
+ run: |
+ echo "Revoking GitHub App installation token..."
+ # GitHub CLI will auth with the token being revoked.
+ gh api \
+ --method DELETE \
+ -H "Authorization: token $TOKEN" \
+ /installation/token || echo "Token revoke may already be expired."
+
+ echo "Token invalidation step complete."
pre_activation:
runs-on: ubuntu-slim
@@ -1197,7 +1099,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1206,10 +1108,19 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
+ - name: Generate GitHub App token for skip-if checks
+ id: pre-activation-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
- name: Check skip-if-match query
id: check_skip_if_match
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1218,10 +1129,11 @@ jobs:
GH_AW_WORKFLOW_NAME: "Daily Testify Uber Super Expert"
GH_AW_SKIP_MAX_MATCHES: "1"
with:
+ github-token: ${{ steps.pre-activation-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_skip_if_match.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_skip_if_match.cjs');
await main();
push_repo_memory:
@@ -1233,6 +1145,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1248,7 +1162,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1291,9 +1205,9 @@ jobs:
FILE_GLOB_FILTER: "memory/testify-expert/*.json memory/testify-expert/*.txt"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1307,6 +1221,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-testify-uber-super-expert"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-testify-uber-super-expert"
GH_AW_WORKFLOW_ID: "daily-testify-uber-super-expert"
GH_AW_WORKFLOW_NAME: "Daily Testify Uber Super Expert"
@@ -1330,7 +1245,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1344,6 +1259,17 @@ jobs:
mkdir -p /tmp/gh-aw/
find "/tmp/gh-aw/" -type f -print
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV"
+ - name: Generate GitHub App token
+ id: safe-outputs-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
+ permission-contents: read
+ permission-issues: write
- name: Process Safe Outputs
id: process_safe_outputs
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1354,12 +1280,25 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"expires\":48,\"labels\":[\"testing\",\"code-quality\",\"automated-analysis\",\"cookie\"],\"max\":1,\"title_prefix\":\"[testify-expert] \"},\"missing_data\":{},\"missing_tool\":{}}"
with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ github-token: ${{ steps.safe-outputs-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
+ - name: Invalidate GitHub App token
+ if: always() && steps.safe-outputs-app-token.outputs.token != ''
+ env:
+ TOKEN: ${{ steps.safe-outputs-app-token.outputs.token }}
+ run: |
+ echo "Revoking GitHub App installation token..."
+ # GitHub CLI will auth with the token being revoked.
+ gh api \
+ --method DELETE \
+ -H "Authorization: token $TOKEN" \
+ /installation/token || echo "Token revoke may already be expired."
+
+ echo "Token invalidation step complete."
- name: Upload Safe Output Items Manifest
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
diff --git a/.github/workflows/daily-testify-uber-super-expert.md b/.github/workflows/daily-testify-uber-super-expert.md
index b3da4c376a1..f9251ee7168 100644
--- a/.github/workflows/daily-testify-uber-super-expert.md
+++ b/.github/workflows/daily-testify-uber-super-expert.md
@@ -184,11 +184,11 @@ Create a detailed issue with this structure:
```markdown
# Improve Test Quality: [FILE_PATH]
-## Overview
+### Overview
The test file `[FILE_PATH]` has been selected for quality improvement by the Testify Uber Super Expert. This issue provides specific, actionable recommendations to enhance test quality, coverage, and maintainability using testify best practices.
-## Current State
+### Current State
- **Test File**: `[FILE_PATH]`
- **Source File**: `[SOURCE_FILE]` (if exists)
@@ -196,12 +196,15 @@ The test file `[FILE_PATH]` has been selected for quality improvement by the Tes
- **Lines of Code**: [LOC] lines
- **Last Modified**: [DATE if available]
-## Test Quality Analysis
+### Test Quality Analysis
### Strengths ✅
[List 2-3 things the test file does well]
+
+🎯 Areas for Improvement
+
### Areas for Improvement 🎯
#### 1. Testify Assertions
@@ -334,9 +337,12 @@ require.NoError(t, err, "setup should succeed without errors")
**Why this matters**: Good assertion messages make test failures easier to debug.
-## Implementation Guidelines
+
+
+
+📋 Implementation Guidelines
-### Priority Order
+#### Priority Order
1. **High**: Add missing tests for critical functions
2. **High**: Convert manual error checks to testify assertions
3. **Medium**: Refactor similar tests into table-driven tests
@@ -362,7 +368,9 @@ go test -cover [PACKAGE_PATH]
make test-unit
```
-## Acceptance Criteria
+
+
+### Acceptance Criteria
- [ ] All manual error checks replaced with testify assertions (`require.NoError`, `assert.Equal`, etc.)
- [ ] Similar test functions refactored into table-driven tests
@@ -372,7 +380,7 @@ make test-unit
- [ ] Tests pass: `make test-unit`
- [ ] Code follows patterns in `scratchpad/testing.md`
-## Additional Context
+### Additional Context
- **Repository Testing Guidelines**: See `scratchpad/testing.md` for comprehensive testing patterns
- **Example Tests**: Look at recent test files in `pkg/workflow/*_test.go` for examples
diff --git a/.github/workflows/daily-workflow-updater.lock.yml b/.github/workflows/daily-workflow-updater.lock.yml
index cc4f69298fd..6a734917f43 100644
--- a/.github/workflows/daily-workflow-updater.lock.yml
+++ b/.github/workflows/daily-workflow-updater.lock.yml
@@ -59,7 +59,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -74,14 +74,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github","go"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -98,9 +98,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "daily-workflow-updater.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -115,20 +115,20 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -160,6 +160,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -173,9 +174,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -191,10 +192,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -213,11 +214,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -243,10 +244,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dailyworkflowupdater
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -268,13 +270,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -297,16 +303,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -315,167 +321,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request":{"expires":24,"max":1,"title_prefix":"[actions] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[actions] \". Labels [\"dependencies\" \"automation\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[actions] \". Labels [\"dependencies\" \"automation\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -572,6 +441,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -596,8 +466,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -608,7 +478,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -616,7 +486,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -634,10 +505,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -645,10 +516,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -656,6 +532,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -673,7 +556,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -682,7 +566,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -711,7 +595,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -749,15 +633,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -766,7 +650,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -778,14 +662,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -794,18 +678,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -881,9 +765,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -906,7 +790,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -934,9 +818,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -980,6 +864,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-daily-workflow-updater"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -995,7 +881,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1020,9 +906,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1034,9 +920,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1058,9 +944,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1076,9 +962,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1091,9 +977,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
safe_outputs:
@@ -1110,6 +996,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-workflow-updater"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "daily-workflow-updater"
GH_AW_WORKFLOW_ID: "daily-workflow-updater"
GH_AW_WORKFLOW_NAME: "Daily Workflow Updater"
@@ -1133,7 +1020,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1180,7 +1067,7 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"draft\":false,\"expires\":24,\"labels\":[\"dependencies\",\"automation\"],\"max\":1,\"max_patch_size\":1024,\"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\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"title_prefix\":\"[actions] \"},\"missing_data\":{},\"missing_tool\":{}}"
@@ -1188,9 +1075,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/daily-workflow-updater.md b/.github/workflows/daily-workflow-updater.md
index 394ee4fd293..054cdaf2ab2 100644
--- a/.github/workflows/daily-workflow-updater.md
+++ b/.github/workflows/daily-workflow-updater.md
@@ -119,16 +119,21 @@ If `.github/aw/actions-lock.json` has changes:
**PR Body Template**:
```markdown
-## GitHub Actions Updates - [Date]
+### GitHub Actions Updates - [Date]
This PR updates GitHub Actions versions in `.github/aw/actions-lock.json` to their latest compatible releases.
+
+📦 Actions Updated (full list)
+
### Actions Updated
[List each action that was updated with before/after versions, e.g.:]
- `actions/checkout`: v4 → v5
- `actions/setup-node`: v5 → v6
+
+
### Summary
- **Total actions updated**: [number]
diff --git a/.github/workflows/dead-code-remover.lock.yml b/.github/workflows/dead-code-remover.lock.yml
index e286dfc042e..e4ddfb25221 100644
--- a/.github/workflows/dead-code-remover.lock.yml
+++ b/.github/workflows/dead-code-remover.lock.yml
@@ -27,7 +27,7 @@
# Imports:
# - shared/activation-app.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"00f650dd3f0d6abf021fc2e0e683c25aa13ee283334637072f1d6def43026a6a","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"3956b456cfdacc8d07c453a70dff9e8f4afda257b5a6f358491e19a2375b1a6d","strict":true}
name: "Dead Code Removal Agent"
"on":
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,14 +81,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","go"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -105,9 +105,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "dead-code-remover.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -123,21 +123,21 @@ jobs:
GH_AW_GITHUB_SERVER_URL: ${{ github.server_url }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -169,6 +169,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -188,9 +189,9 @@ jobs:
GH_AW_GITHUB_SERVER_URL: ${{ github.server_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -211,10 +212,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -238,11 +239,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -268,10 +269,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: deadcoderemover
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -293,7 +295,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -306,13 +308,17 @@ jobs:
- name: Capture GOROOT for AWF chroot mode
run: echo "GOROOT=$(go env GOROOT)" >> "$GITHUB_ENV"
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Install deadcode analyzer
run: go install golang.org/x/tools/cmd/deadcode@latest
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -342,186 +348,48 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
- CUSTOM_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request":{"expires":72,"max":1,"reviewers":["copilot"],"title_prefix":"[dead-code] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[dead-code] \". Labels [\"chore\" \"dead-code\"] will be automatically added. Reviewers [\"copilot\"] will be assigned.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[dead-code] \". Labels [\"chore\" \"dead-code\"] will be automatically added. Reviewers [\"copilot\"] will be assigned."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -618,6 +486,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -642,8 +511,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -654,7 +523,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -662,7 +531,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -680,10 +550,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -691,10 +561,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -702,6 +577,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -719,7 +601,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -728,7 +611,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -757,7 +640,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -795,15 +678,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -812,7 +695,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -829,9 +712,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -840,18 +723,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -933,9 +816,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -958,7 +841,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -986,9 +869,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1033,6 +916,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-dead-code-remover"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1048,7 +933,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1072,9 +957,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1085,9 +970,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1108,9 +993,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1125,9 +1010,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1139,9 +1024,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
pre_activation:
@@ -1162,7 +1047,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1171,10 +1056,19 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
+ - name: Generate GitHub App token for skip-if checks
+ id: pre-activation-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
- name: Check skip-if-match query
id: check_skip_if_match
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1183,10 +1077,11 @@ jobs:
GH_AW_WORKFLOW_NAME: "Dead Code Removal Agent"
GH_AW_SKIP_MAX_MATCHES: "1"
with:
+ github-token: ${{ steps.pre-activation-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_skip_if_match.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_skip_if_match.cjs');
await main();
safe_outputs:
@@ -1203,6 +1098,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/dead-code-remover"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "dead-code-remover"
GH_AW_WORKFLOW_NAME: "Dead Code Removal Agent"
outputs:
@@ -1225,7 +1121,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1280,9 +1176,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1299,6 +1195,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: deadcoderemover
steps:
- name: Checkout actions folder
@@ -1311,7 +1208,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml
index f4b6f88ca58..cb66497081d 100644
--- a/.github/workflows/deep-report.lock.yml
+++ b/.github/workflows/deep-report.lock.yml
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,18 +80,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","python","node"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate CODEX_API_KEY or OPENAI_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
@@ -110,9 +110,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "deep-report.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -128,17 +128,18 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, create_discussion, upload_asset, missing_tool, missing_data, noop
@@ -174,6 +175,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -196,9 +198,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -223,10 +225,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -254,11 +256,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -286,10 +288,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: deepreport
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -310,7 +313,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -346,7 +349,11 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
- env:
@@ -357,7 +364,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -374,7 +381,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -397,9 +404,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -409,7 +416,7 @@ jobs:
- name: Install Codex
run: npm install -g @openai/codex@latest
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -418,10 +425,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -436,247 +443,38 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":168,"max":1},"create_issue":{"expires":48,"group":true,"max":3},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":1048576,"max_patch_size":10240}]},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 3 issue(s) can be created. Title will be prefixed with \"[deep-report] \". Labels [\"automation\" \"improvement\" \"quick-win\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"reports\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"reports\".",
+ "create_issue": " CONSTRAINTS: Maximum 3 issue(s) can be created. Title will be prefixed with \"[deep-report] \". Labels [\"automation\" \"improvement\" \"quick-win\" \"cookie\"] will be automatically added.",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -805,6 +603,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -829,8 +628,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -841,7 +640,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -852,7 +651,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -871,7 +671,7 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="codex"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
cat > /tmp/gh-aw/mcp-config/config.toml << GH_AW_MCP_CONFIG_EOF
[history]
@@ -886,6 +686,11 @@ jobs:
mounts = ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"]
env_vars = ["DEBUG", "GH_TOKEN", "GITHUB_TOKEN", "GITHUB_ACTOR", "GITHUB_REPOSITORY"]
+ [mcp_servers.agenticworkflows."guard-policies"]
+
+ [mcp_servers.agenticworkflows."guard-policies".write-sink]
+ accept = ["*"]
+
[mcp_servers.github]
user_agent = "deepreport-intelligence-gathering-agent"
startup_timeout_sec = 120
@@ -900,10 +705,15 @@ jobs:
[mcp_servers.safeoutputs.headers]
Authorization = "$GH_AW_SAFE_OUTPUTS_API_KEY"
+
+ [mcp_servers.safeoutputs."guard-policies"]
+
+ [mcp_servers.safeoutputs."guard-policies".write-sink]
+ accept = ["*"]
GH_AW_MCP_CONFIG_EOF
# Generate JSON config for MCP gateway
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -915,16 +725,28 @@ jobs:
"GITHUB_TOKEN": "$GITHUB_TOKEN",
"GITHUB_ACTOR": "$GITHUB_ACTOR",
"GITHUB_REPOSITORY": "$GITHUB_REPOSITORY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "all"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -932,6 +754,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -949,13 +778,14 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Codex
run: |
set -o pipefail
mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,*.pythonhosted.org,172.30.0.1,anaconda.org,api.npms.io,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,*.pythonhosted.org,172.30.0.1,anaconda.org,api.npms.io,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" && codex ${GH_AW_MODEL_AGENT_CODEX:+-c model="$GH_AW_MODEL_AGENT_CODEX" }exec -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
@@ -997,15 +827,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'CODEX_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY'
@@ -1016,7 +846,7 @@ jobs:
SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1033,9 +863,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1044,18 +874,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_codex_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_codex_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1154,9 +984,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1169,7 +999,7 @@ jobs:
set -o pipefail
mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,host.docker.internal,openai.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,host.docker.internal,openai.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" && codex ${GH_AW_MODEL_DETECTION_CODEX:+-c model="$GH_AW_MODEL_DETECTION_CODEX" }exec -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
@@ -1193,9 +1023,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1242,6 +1072,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-deep-report"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1257,7 +1089,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1282,9 +1114,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1296,9 +1128,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1324,9 +1156,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1342,9 +1174,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1356,6 +1188,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1371,7 +1205,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1414,9 +1248,9 @@ jobs:
FILE_GLOB_FILTER: "memory/deep-report/*.md"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1431,6 +1265,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/deep-report"
GH_AW_ENGINE_ID: "codex"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "deep-report-intel-agent"
GH_AW_WORKFLOW_ID: "deep-report"
GH_AW_WORKFLOW_NAME: "DeepReport - Intelligence Gathering Agent"
@@ -1454,7 +1289,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1480,9 +1315,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1499,6 +1334,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: deepreport
steps:
- name: Checkout actions folder
@@ -1511,7 +1347,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1556,7 +1392,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1612,8 +1448,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/delight.lock.yml b/.github/workflows/delight.lock.yml
index f536314e9f4..6c485ad3418 100644
--- a/.github/workflows/delight.lock.yml
+++ b/.github/workflows/delight.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,14 +79,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -103,9 +103,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "delight.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -121,16 +121,16 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, create_discussion, missing_tool, missing_data, noop
@@ -164,6 +164,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -185,9 +186,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -209,10 +210,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -237,11 +238,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -268,10 +269,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: delight
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -293,13 +295,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
@@ -312,7 +318,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -335,16 +341,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -353,216 +359,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":168,"max":1},"create_issue":{"expires":48,"group":true,"max":2},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 2 issue(s) can be created. Labels [\"delight\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
+ "create_issue": " CONSTRAINTS: Maximum 2 issue(s) can be created. Labels [\"delight\" \"cookie\"] will be automatically added."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -682,6 +503,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -706,8 +528,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -718,7 +540,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -726,7 +548,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -744,10 +567,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -755,10 +578,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,discussions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -766,6 +594,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -783,7 +618,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -815,7 +651,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(./gh-aw --help)'\'' --allow-tool '\''shell(/tmp/gh-aw/jqschema.sh)'\'' --allow-tool '\''shell(cat *)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find .github/workflows -name '\''\'\'''\''*.md'\''\'\'''\'')'\'' --allow-tool '\''shell(find docs -name '\''\'\'''\''*.md'\''\'\'''\'' -o -name '\''\'\'''\''*.mdx'\''\'\'''\'')'\'' --allow-tool '\''shell(git:*)'\'' --allow-tool '\''shell(grep -r '\''\'\'''\''*'\''\'\'''\'' docs)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq *)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -844,7 +680,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -882,15 +718,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -899,7 +735,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -911,14 +747,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -927,18 +763,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1022,9 +858,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1047,7 +883,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1075,9 +911,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1122,6 +958,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-delight"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1137,7 +975,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1162,9 +1000,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1176,9 +1014,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1205,9 +1043,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1223,9 +1061,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1237,6 +1075,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1252,7 +1092,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1295,9 +1135,9 @@ jobs:
FILE_GLOB_FILTER: "memory/delight/*.json memory/delight/*.md"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1312,6 +1152,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/delight"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 📊 *User experience analysis by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"📊 Delight Agent starting! [{workflow_name}]({run_url}) is analyzing user-facing aspects for improvement opportunities...\",\"runSuccess\":\"✅ Analysis complete! [{workflow_name}]({run_url}) has identified targeted improvements for user experience.\",\"runFailure\":\"⚠️ Analysis interrupted! [{workflow_name}]({run_url}) {status}. Please review the logs...\"}"
GH_AW_TRACKER_ID: "delight-daily"
GH_AW_WORKFLOW_ID: "delight"
@@ -1336,7 +1177,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1355,16 +1196,16 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":168,\"fallback_to_issue\":true,\"max\":1},\"create_issue\":{\"expires\":48,\"group\":true,\"labels\":[\"delight\",\"cookie\"],\"max\":2},\"missing_data\":{},\"missing_tool\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/dependabot-burner.lock.yml b/.github/workflows/dependabot-burner.lock.yml
index 66b93ef8ef0..e0ee0ecab44 100644
--- a/.github/workflows/dependabot-burner.lock.yml
+++ b/.github/workflows/dependabot-burner.lock.yml
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,18 +80,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -109,9 +109,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "dependabot-burner.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -126,15 +126,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -168,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -184,9 +185,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -203,10 +204,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -226,11 +227,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -255,10 +256,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dependabotburner
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -280,13 +282,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -309,16 +315,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -327,167 +333,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[dependabot-burner] \".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[dependabot-burner] \"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -581,6 +450,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -605,8 +475,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -617,7 +487,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -625,7 +495,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -643,10 +514,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -654,10 +525,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -665,6 +541,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -682,7 +565,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -691,7 +575,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -719,7 +603,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -757,15 +641,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -775,7 +659,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -792,9 +676,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -803,18 +687,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -889,9 +773,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -914,7 +798,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -941,9 +825,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -986,6 +870,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-dependabot-burner"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1001,7 +887,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1025,9 +911,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1038,9 +924,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1060,9 +946,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1077,9 +963,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
pre_activation:
@@ -1100,7 +986,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1109,9 +995,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
safe_outputs:
@@ -1125,6 +1011,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/dependabot-burner"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "dependabot-burner"
GH_AW_WORKFLOW_NAME: "Dependabot Burner"
outputs:
@@ -1147,7 +1034,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1173,9 +1060,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/dependabot-go-checker.lock.yml b/.github/workflows/dependabot-go-checker.lock.yml
index bfbfb79417a..876ac501158 100644
--- a/.github/workflows/dependabot-go-checker.lock.yml
+++ b/.github/workflows/dependabot-go-checker.lock.yml
@@ -63,7 +63,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -78,18 +78,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -107,9 +107,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "dependabot-go-checker.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -124,15 +124,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, close_issue, missing_tool, missing_data, noop
@@ -166,6 +166,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -183,9 +184,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -201,10 +202,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -223,11 +224,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -254,10 +255,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dependabotgochecker
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -279,13 +281,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -308,16 +314,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -326,199 +332,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"close_issue":{"max":20,"required_title_prefix":"[deps]","target":"*"},"create_issue":{"expires":48,"group":true,"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 10 issue(s) can be created. Title will be prefixed with \"[deps]\". Labels [\"dependencies\" \"go\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Close a GitHub issue with a closing comment. You can and should always add a comment when closing an issue to explain the action or provide context. This tool is ONLY for closing issues - use update_issue if you need to change the title, body, labels, or other metadata without closing. Use close_issue when work is complete, the issue is no longer relevant, or it's a duplicate. The closing comment should explain the resolution or reason for closing. If the issue is already closed, a comment will still be posted. CONSTRAINTS: Maximum 20 issue(s) can be closed. Target: *. Only issues with title prefix \"[deps]\" can be closed.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Closing comment explaining why the issue is being closed and summarizing any resolution, workaround, or conclusion.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "issue_number": {
- "description": "Issue number to close. This is the numeric ID from the GitHub URL (e.g., 901 in github.com/owner/repo/issues/901). If omitted, closes the issue that triggered this workflow (requires an issue event trigger).",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "close_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "close_issue": " CONSTRAINTS: Maximum 20 issue(s) can be closed. Target: *. Only issues with title prefix \"[deps]\" can be closed.",
+ "create_issue": " CONSTRAINTS: Maximum 10 issue(s) can be created. Title will be prefixed with \"[deps]\". Labels [\"dependencies\" \"go\" \"cookie\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"close_issue": {
"defaultMax": 1,
@@ -630,6 +468,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -654,8 +493,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -666,7 +505,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -674,7 +513,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -692,10 +532,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -703,10 +543,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,dependabot"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -714,6 +559,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -731,7 +583,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -740,7 +593,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -768,7 +621,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -806,15 +659,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -824,7 +677,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -841,9 +694,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -852,18 +705,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -938,9 +791,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -963,7 +816,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -990,9 +843,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1035,6 +888,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-dependabot-go-checker"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1050,7 +905,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1074,9 +929,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1087,9 +942,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1109,9 +964,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1126,9 +981,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1142,6 +997,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/dependabot-go-checker"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "dependabot-go-checker"
GH_AW_WORKFLOW_NAME: "Dependabot Dependency Checker"
outputs:
@@ -1164,7 +1020,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1190,9 +1046,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/dev-hawk.lock.yml b/.github/workflows/dev-hawk.lock.yml
index 527ea46e769..2d40441cf53 100644
--- a/.github/workflows/dev-hawk.lock.yml
+++ b/.github/workflows/dev-hawk.lock.yml
@@ -69,7 +69,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -84,14 +84,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "dev-hawk.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -131,15 +131,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, missing_tool, missing_data, noop
@@ -173,6 +174,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -193,9 +195,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -218,10 +220,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -247,11 +249,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -277,10 +279,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: devhawk
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -302,7 +305,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -338,7 +341,11 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -361,16 +368,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -379,10 +386,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -397,161 +404,36 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":1,"target":"*"},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added. Target: *.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Target: *."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -630,6 +512,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -654,8 +537,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -666,7 +549,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -674,7 +557,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -693,10 +577,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -709,6 +593,13 @@ jobs:
"GITHUB_TOKEN": "\${GITHUB_TOKEN}",
"GITHUB_ACTOR": "\${GITHUB_ACTOR}",
"GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
@@ -716,10 +607,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "pull_requests,actions,repos"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -727,6 +623,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -744,7 +647,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -769,7 +673,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(gh agent-task create *)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -798,7 +702,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -836,15 +740,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -853,7 +757,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -870,9 +774,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -881,18 +785,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -967,9 +871,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -992,7 +896,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1020,9 +924,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1067,6 +971,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-dev-hawk"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1082,7 +988,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1106,9 +1012,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1119,9 +1025,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1141,9 +1047,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1158,9 +1064,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
pre_activation:
@@ -1182,7 +1088,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1191,9 +1097,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
safe_outputs:
@@ -1209,6 +1115,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/dev-hawk"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🦅 *Observed from above by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🦅 Dev Hawk circles the sky! [{workflow_name}]({run_url}) is monitoring this {event_type} from above...\",\"runSuccess\":\"🦅 Hawk eyes report! [{workflow_name}]({run_url}) has completed reconnaissance. Intel delivered! 🎯\",\"runFailure\":\"🦅 Hawk down! [{workflow_name}]({run_url}) {status}. The skies grow quiet...\"}"
GH_AW_WORKFLOW_ID: "dev-hawk"
GH_AW_WORKFLOW_NAME: "Dev Hawk"
@@ -1232,7 +1139,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1258,9 +1165,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml
index 26fe366a873..598db3db414 100644
--- a/.github/workflows/dev.lock.yml
+++ b/.github/workflows/dev.lock.yml
@@ -58,7 +58,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -73,14 +73,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "false"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -97,9 +97,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "dev.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -114,15 +114,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -156,6 +156,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -169,9 +170,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -187,10 +188,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -209,11 +210,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -239,10 +240,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dev
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -264,13 +266,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -293,16 +299,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -311,167 +317,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":168,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[Daily Report] \".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[Daily Report] \"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -565,6 +434,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -589,8 +459,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -601,7 +471,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -609,7 +479,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -627,10 +498,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -638,10 +509,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -649,6 +525,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -666,7 +549,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -675,7 +559,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -704,7 +588,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -742,15 +626,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -759,7 +643,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -776,9 +660,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -787,18 +671,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -873,9 +757,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -898,7 +782,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -926,9 +810,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -971,6 +855,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-dev"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -986,7 +872,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1010,9 +896,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1023,9 +909,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1044,9 +930,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1061,9 +947,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1077,6 +963,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/dev"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "dev"
GH_AW_WORKFLOW_NAME: "Dev"
outputs:
@@ -1099,7 +986,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1125,9 +1012,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml
index afd6ddbbe4d..2e3249fe953 100644
--- a/.github/workflows/developer-docs-consolidator.lock.yml
+++ b/.github/workflows/developer-docs-consolidator.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,18 +81,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -110,9 +110,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "developer-docs-consolidator.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -127,22 +127,22 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -174,6 +174,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -198,9 +199,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -225,10 +226,10 @@ jobs:
GH_AW_WIKI_NOTE: "\n\n> **GitHub Wiki**: This memory is backed by the GitHub Wiki for this repository. Files use GitHub Wiki Markdown syntax. Follow GitHub Wiki conventions when creating or editing pages (e.g., use standard Markdown headers, use `[[Page Name]]` syntax for internal wiki links, name page files with spaces replaced by hyphens or use the wiki page title as the filename)."
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -256,11 +257,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -286,10 +287,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: developerdocsconsolidator
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -310,7 +312,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -321,7 +323,11 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Install QMD
run: npm install -g @tobilu/qmd
- name: Restore QMD index cache
@@ -336,7 +342,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -353,7 +359,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}.wiki
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: false
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -376,9 +382,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -386,7 +392,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -397,216 +403,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":168,"max":1},"create_pull_request":{"expires":48,"max":1,"title_prefix":"[docs] "},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[docs] \". Labels [\"documentation\" \"automation\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[docs] \". Labels [\"documentation\" \"automation\"] will be automatically added."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -729,6 +550,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -753,8 +575,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -765,16 +587,16 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Setup MCP Scripts Config
run: |
- mkdir -p /opt/gh-aw/mcp-scripts/logs
- cat > /opt/gh-aw/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
+ mkdir -p ${GH_AW_HOME}/mcp-scripts/logs
+ cat > ${GH_AW_HOME}/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
{
"serverName": "mcpscripts",
"version": "1.0.0",
- "logDir": "/opt/gh-aw/mcp-scripts/logs",
+ "logDir": "${GH_AW_HOME}/mcp-scripts/logs",
"tools": [
{
"name": "qmd-query",
@@ -802,7 +624,7 @@ jobs:
]
}
GH_AW_MCP_SCRIPTS_TOOLS_EOF
- cat > /opt/gh-aw/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
const path = require("path");
const { startHttpServer } = require("./mcp_scripts_mcp_server_http.cjs");
const configPath = path.join(__dirname, "tools.json");
@@ -811,17 +633,17 @@ jobs:
startHttpServer(configPath, {
port: port,
stateless: true,
- logDir: "/opt/gh-aw/mcp-scripts/logs"
+ logDir: process.env.GH_AW_HOME + "/mcp-scripts/logs"
}).catch(error => {
console.error("Failed to start mcp-scripts HTTP server:", error);
process.exit(1);
});
GH_AW_MCP_SCRIPTS_SERVER_EOF
- chmod +x /opt/gh-aw/mcp-scripts/mcp-server.cjs
+ chmod +x ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs
- name: Setup MCP Scripts Tool Files
run: |
- cat > /opt/gh-aw/mcp-scripts/qmd-query.sh << 'GH_AW_MCP_SCRIPTS_SH_QMD-QUERY_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/qmd-query.sh << 'GH_AW_MCP_SCRIPTS_SH_QMD-QUERY_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: qmd-query
# Find relevant file paths in project documentation using vector similarity search. Returns file paths and scores.
@@ -833,7 +655,7 @@ jobs:
GH_AW_MCP_SCRIPTS_SH_QMD-QUERY_EOF
- chmod +x /opt/gh-aw/mcp-scripts/qmd-query.sh
+ chmod +x ${GH_AW_HOME}/mcp-scripts/qmd-query.sh
- name: Generate MCP Scripts Server Config
id: mcp-scripts-config
@@ -865,7 +687,7 @@ jobs:
export GH_AW_MCP_SCRIPTS_PORT
export GH_AW_MCP_SCRIPTS_API_KEY
- bash /opt/gh-aw/actions/start_mcp_scripts_server.sh
+ bash ${GH_AW_HOME}/actions/start_mcp_scripts_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -875,7 +697,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -893,19 +716,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"mcpscripts": {
@@ -913,6 +741,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_MCP_SCRIPTS_PORT",
"headers": {
"Authorization": "$GH_AW_MCP_SCRIPTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"safeoutputs": {
@@ -920,6 +755,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -936,7 +778,14 @@ jobs:
"--project",
"\${GITHUB_WORKSPACE}"
],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -953,7 +802,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -1058,7 +908,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(cat scratchpad/*.md),Bash(cat),Bash(date),Bash(echo),Bash(find specs -maxdepth 1 -ls),Bash(find specs -name '\''\'\'''\''*.md'\''\'\'''\''),Bash(git add:*),Bash(git branch:*),Bash(git checkout:*),Bash(git commit:*),Bash(git merge:*),Bash(git rm:*),Bash(git status),Bash(git switch:*),Bash(git),Bash(grep -r '\''\'\'''\''*'\''\'\'''\'' specs),Bash(grep),Bash(head),Bash(ls),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc -l scratchpad/*.md),Bash(wc),Bash(yq),BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1102,15 +952,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -1120,7 +970,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1132,14 +982,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1148,27 +998,27 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Scripts logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_scripts_logs.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_scripts_logs.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1258,9 +1108,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1293,7 +1143,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1321,9 +1171,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1370,6 +1220,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-developer-docs-consolidator"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1385,7 +1237,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1409,9 +1261,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1422,9 +1274,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1451,9 +1303,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1468,9 +1320,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1482,9 +1334,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
push_repo_memory:
@@ -1496,6 +1348,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1511,7 +1365,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1554,9 +1408,9 @@ jobs:
ALLOWED_EXTENSIONS: '[]'
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1574,6 +1428,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/developer-docs-consolidator"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "developer-docs-consolidator"
GH_AW_WORKFLOW_NAME: "Developer Documentation Consolidator"
outputs:
@@ -1596,7 +1451,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1643,7 +1498,7 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":168,\"fallback_to_issue\":true,\"max\":1},\"create_pull_request\":{\"draft\":false,\"expires\":48,\"labels\":[\"documentation\",\"automation\"],\"max\":1,\"max_patch_size\":1024,\"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\",\"CLAUDE.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\",\".claude/\"],\"title_prefix\":\"[docs] \"},\"missing_data\":{},\"missing_tool\":{}}"
@@ -1651,9 +1506,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1670,6 +1525,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: developerdocsconsolidator
steps:
- name: Checkout actions folder
@@ -1682,7 +1538,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/dictation-prompt.lock.yml b/.github/workflows/dictation-prompt.lock.yml
index 9df309adf02..9532f219673 100644
--- a/.github/workflows/dictation-prompt.lock.yml
+++ b/.github/workflows/dictation-prompt.lock.yml
@@ -63,7 +63,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -78,14 +78,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -102,9 +102,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "dictation-prompt.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -119,20 +119,20 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -164,6 +164,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -183,9 +184,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -201,10 +202,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -223,11 +224,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -253,10 +254,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: dictationprompt
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -278,7 +280,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -289,7 +291,11 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Install QMD
run: npm install -g @tobilu/qmd
- name: Restore QMD index cache
@@ -324,16 +330,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -342,167 +348,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request":{"auto_merge":true,"expires":48,"max":1,"title_prefix":"[docs] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[docs] \". Labels [\"documentation\" \"automation\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[docs] \". Labels [\"documentation\" \"automation\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -599,6 +468,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -623,8 +493,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -635,16 +505,16 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Setup MCP Scripts Config
run: |
- mkdir -p /opt/gh-aw/mcp-scripts/logs
- cat > /opt/gh-aw/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
+ mkdir -p ${GH_AW_HOME}/mcp-scripts/logs
+ cat > ${GH_AW_HOME}/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
{
"serverName": "mcpscripts",
"version": "1.0.0",
- "logDir": "/opt/gh-aw/mcp-scripts/logs",
+ "logDir": "${GH_AW_HOME}/mcp-scripts/logs",
"tools": [
{
"name": "qmd-query",
@@ -672,7 +542,7 @@ jobs:
]
}
GH_AW_MCP_SCRIPTS_TOOLS_EOF
- cat > /opt/gh-aw/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
const path = require("path");
const { startHttpServer } = require("./mcp_scripts_mcp_server_http.cjs");
const configPath = path.join(__dirname, "tools.json");
@@ -681,17 +551,17 @@ jobs:
startHttpServer(configPath, {
port: port,
stateless: true,
- logDir: "/opt/gh-aw/mcp-scripts/logs"
+ logDir: process.env.GH_AW_HOME + "/mcp-scripts/logs"
}).catch(error => {
console.error("Failed to start mcp-scripts HTTP server:", error);
process.exit(1);
});
GH_AW_MCP_SCRIPTS_SERVER_EOF
- chmod +x /opt/gh-aw/mcp-scripts/mcp-server.cjs
+ chmod +x ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs
- name: Setup MCP Scripts Tool Files
run: |
- cat > /opt/gh-aw/mcp-scripts/qmd-query.sh << 'GH_AW_MCP_SCRIPTS_SH_QMD-QUERY_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/qmd-query.sh << 'GH_AW_MCP_SCRIPTS_SH_QMD-QUERY_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: qmd-query
# Find relevant file paths in project documentation using vector similarity search. Returns file paths and scores.
@@ -703,7 +573,7 @@ jobs:
GH_AW_MCP_SCRIPTS_SH_QMD-QUERY_EOF
- chmod +x /opt/gh-aw/mcp-scripts/qmd-query.sh
+ chmod +x ${GH_AW_HOME}/mcp-scripts/qmd-query.sh
- name: Generate MCP Scripts Server Config
id: mcp-scripts-config
@@ -735,7 +605,7 @@ jobs:
export GH_AW_MCP_SCRIPTS_PORT
export GH_AW_MCP_SCRIPTS_API_KEY
- bash /opt/gh-aw/actions/start_mcp_scripts_server.sh
+ bash ${GH_AW_HOME}/actions/start_mcp_scripts_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -745,7 +615,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -763,10 +634,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -774,10 +645,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"mcpscripts": {
@@ -785,6 +661,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_MCP_SCRIPTS_PORT",
"headers": {
"Authorization": "\${GH_AW_MCP_SCRIPTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"safeoutputs": {
@@ -792,6 +675,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -809,7 +699,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -818,7 +709,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -847,7 +738,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -885,15 +776,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -902,7 +793,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -919,9 +810,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -930,27 +821,27 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Scripts logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_scripts_logs.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_scripts_logs.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1027,9 +918,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1052,7 +943,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1080,9 +971,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1126,6 +1017,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-dictation-prompt"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1141,7 +1034,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1165,9 +1058,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1178,9 +1071,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1201,9 +1094,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1218,9 +1111,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1232,9 +1125,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
safe_outputs:
@@ -1251,6 +1144,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/dictation-prompt"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "dictation-prompt"
GH_AW_WORKFLOW_NAME: "Dictation Prompt Generator"
outputs:
@@ -1273,7 +1167,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1328,9 +1222,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/discussion-task-miner.lock.yml b/.github/workflows/discussion-task-miner.lock.yml
index 3c37219bd98..2883775b088 100644
--- a/.github/workflows/discussion-task-miner.lock.yml
+++ b/.github/workflows/discussion-task-miner.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,7 +79,7 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
@@ -89,7 +89,7 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -106,9 +106,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "discussion-task-miner.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -124,16 +124,16 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, create_issue, missing_tool, missing_data, noop
@@ -167,6 +167,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -186,9 +187,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -210,10 +211,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -238,11 +239,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -269,10 +270,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: discussiontaskminer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -294,13 +296,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
@@ -313,7 +319,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -336,227 +342,49 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":3},"create_issue":{"expires":24,"group":true,"max":5},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 5 issue(s) can be created. Title will be prefixed with \"[Code Quality] \". Labels [\"code-quality\" \"automation\" \"task-mining\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 3 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 3 comment(s) can be added.",
+ "create_issue": " CONSTRAINTS: Maximum 5 issue(s) can be created. Title will be prefixed with \"[Code Quality] \". Labels [\"code-quality\" \"automation\" \"task-mining\"] will be automatically added."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -668,6 +496,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -692,8 +521,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -704,7 +533,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -712,6 +541,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -729,10 +560,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -744,6 +575,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,discussions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -751,6 +588,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -768,7 +612,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -798,7 +643,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(/tmp/gh-aw/jqschema.sh)'\'' --allow-tool '\''shell(cat *)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date *)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find .github -name '\''\'\'''\''*.md'\''\'\'''\'')'\'' --allow-tool '\''shell(git:*)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq *)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -827,7 +672,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -865,15 +710,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -882,7 +727,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -899,9 +744,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -910,18 +755,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1005,9 +850,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1030,7 +875,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1058,9 +903,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1106,6 +951,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-discussion-task-miner"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1121,7 +968,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1146,9 +993,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1160,9 +1007,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1187,9 +1034,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1205,9 +1052,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1219,6 +1066,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1234,7 +1083,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1277,9 +1126,9 @@ jobs:
FILE_GLOB_FILTER: "memory/discussion-task-miner/*.json memory/discussion-task-miner/*.md"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1295,6 +1144,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/discussion-task-miner"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔍 *Task mining by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔍 Discussion Task Miner starting! [{workflow_name}]({run_url}) is scanning discussions for code quality improvements...\",\"runSuccess\":\"✅ Task mining complete! [{workflow_name}]({run_url}) has identified actionable code quality tasks. 📊\",\"runFailure\":\"⚠️ Task mining interrupted! [{workflow_name}]({run_url}) {status}. Please review the logs...\"}"
GH_AW_TRACKER_ID: "discussion-task-miner"
GH_AW_WORKFLOW_ID: "discussion-task-miner"
@@ -1321,7 +1171,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1347,9 +1197,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/docs-noob-tester.lock.yml b/.github/workflows/docs-noob-tester.lock.yml
index b3cbc60c27a..09edda9d36d 100644
--- a/.github/workflows/docs-noob-tester.lock.yml
+++ b/.github/workflows/docs-noob-tester.lock.yml
@@ -28,7 +28,7 @@
# - shared/docs-server-lifecycle.md
# - shared/reporting.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"67d7121754cd87f039eff5782dd31356426aed9c72c09881a79957f3ab7b9580","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"a594f6d74c90b0467a750ff7da349b7cb66df3d83ac97f3dc2b582c87527556b","strict":true}
name: "Documentation Noob Tester"
"on":
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,14 +79,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","node"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -103,9 +103,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "docs-noob-tester.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -120,16 +120,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/playwright_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/playwright_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, upload_asset, missing_tool, missing_data, noop
@@ -165,6 +165,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -186,9 +187,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -204,10 +205,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -226,11 +227,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -256,10 +257,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: docsnoobtester
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -281,13 +283,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -310,16 +316,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -328,177 +334,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcr.microsoft.com/playwright/mcp node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcr.microsoft.com/playwright/mcp node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -594,6 +454,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -618,8 +479,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -630,7 +491,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -641,7 +502,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -660,10 +522,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -671,10 +533,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"playwright": {
@@ -682,13 +549,27 @@ jobs:
"container": "mcr.microsoft.com/playwright/mcp",
"args": ["--init", "--network", "host", "--security-opt", "seccomp=unconfined", "--ipc=host"],
"entrypointArgs": ["--output-dir", "/tmp/gh-aw/mcp-logs/playwright", "--no-sandbox"],
- "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"]
+ "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"safeoutputs": {
"type": "http",
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -706,7 +587,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -715,7 +597,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -728,6 +610,7 @@ jobs:
GH_AW_PHASE: agent
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_TOOL_TIMEOUT: 120
GH_AW_VERSION: dev
GITHUB_API_URL: ${{ github.api_url }}
GITHUB_AW: true
@@ -747,7 +630,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -785,15 +668,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -802,7 +685,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -819,9 +702,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -830,18 +713,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -925,9 +808,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -950,7 +833,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -978,9 +861,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1025,6 +908,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-docs-noob-tester"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1040,7 +925,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1064,9 +949,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1077,9 +962,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1100,9 +985,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1117,9 +1002,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1134,6 +1019,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/docs-noob-tester"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "docs-noob-tester"
GH_AW_WORKFLOW_NAME: "Documentation Noob Tester"
outputs:
@@ -1154,7 +1040,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1180,9 +1066,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1213,7 +1099,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1268,8 +1154,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/docs-noob-tester.md b/.github/workflows/docs-noob-tester.md
index ca9484595da..a4e719af95b 100644
--- a/.github/workflows/docs-noob-tester.md
+++ b/.github/workflows/docs-noob-tester.md
@@ -11,6 +11,7 @@ permissions:
engine: copilot
timeout-minutes: 30
tools:
+ timeout: 120 # Playwright navigation on Astro dev server can take >60s; increase to 120s
playwright:
edit:
bash:
@@ -62,16 +63,48 @@ Follow the shared **Documentation Server Lifecycle Management** instructions:
1. Start the preview server (section "Starting the Documentation Preview Server")
2. Wait for server readiness (section "Waiting for Server Readiness")
+**Get the bridge IP for Playwright access** (run this after the server is ready):
+
+```bash
+SERVER_IP=$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{print $7; exit}')
+if [ -z "$SERVER_IP" ]; then SERVER_IP=$(hostname -I | awk '{print $1}'); fi
+echo "Playwright server URL: http://${SERVER_IP}:4321/gh-aw/"
+```
+
+Use `http://${SERVER_IP}:4321/gh-aw/` (NOT `localhost:4321`) for all Playwright navigation below.
+
## Step 2: Navigate Documentation as a Noob
+**IMPORTANT: Using Playwright in gh-aw Workflows**
+
+Playwright is provided through an MCP server interface. Use the bridge IP obtained in Step 1 for all navigation:
+
+- ✅ **Correct**: `browser_run_code` with `page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 })`
+- ✅ **Correct**: `browser_navigate` to `http://${SERVER_IP}:4321/gh-aw/` (use the bridge IP, NOT localhost)
+- ❌ **Incorrect**: Using `http://localhost:4321/...` — Playwright runs with `--network host` so its localhost is the Docker host, not the agent container
+
+**⚠️ CRITICAL: Navigation Timeout Prevention**
+
+The Astro development server loads many JavaScript modules per page. Always use `waitUntil: 'domcontentloaded'`:
+
+```javascript
+// ALWAYS use domcontentloaded - replace SERVER_IP with the actual IP from Step 1
+mcp__playwright__browser_run_code({
+ code: `async (page) => {
+ await page.goto('http://SERVER_IP:4321/gh-aw/', { waitUntil: 'domcontentloaded', timeout: 30000 });
+ return { url: page.url(), title: await page.title() };
+ }`
+})
+```
+
Using Playwright, navigate through the documentation site as if you're a complete beginner:
-1. **Visit the home page** at http://localhost:4321/gh-aw/
+1. **Visit the home page** at `http://${SERVER_IP}:4321/gh-aw/`
- Take a screenshot
- Note: Is it immediately clear what this tool does?
- Note: Can you quickly find the "Get Started" or "Quick Start" link?
-2. **Follow the Quick Start Guide** at http://localhost:4321/gh-aw/setup/quick-start/
+2. **Follow the Quick Start Guide** at `http://${SERVER_IP}:4321/gh-aw/setup/quick-start/`
- Take screenshots of each major section
- Try to understand each step from a beginner's perspective
- Questions to consider:
@@ -81,12 +114,12 @@ Using Playwright, navigate through the documentation site as if you're a complet
- Do code examples work as shown?
- Are error messages explained?
-3. **Check the CLI Commands page** at http://localhost:4321/gh-aw/setup/cli/
+3. **Check the CLI Commands page** at `http://${SERVER_IP}:4321/gh-aw/setup/cli/`
- Take a screenshot
- Note: Are the most important commands highlighted?
- Note: Are examples provided for common use cases?
-4. **Explore Creating Workflows guide** at http://localhost:4321/gh-aw/setup/creating-workflows/
+4. **Explore Creating Workflows guide** at `http://${SERVER_IP}:4321/gh-aw/setup/creating-workflows/`
- Take screenshots of confusing sections
- Note: Is the workflow format explained clearly?
- Note: Are there enough examples?
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 8a96f6eb108..f0d0abcd72a 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -68,7 +68,7 @@ jobs:
- name: Install dependencies
working-directory: ./docs
- run: npm ci
+ run: npm ci --legacy-peer-deps
- name: Build documentation
working-directory: ./docs
diff --git a/.github/workflows/draft-pr-cleanup.lock.yml b/.github/workflows/draft-pr-cleanup.lock.yml
index 3fd3891b879..5945240ae87 100644
--- a/.github/workflows/draft-pr-cleanup.lock.yml
+++ b/.github/workflows/draft-pr-cleanup.lock.yml
@@ -59,7 +59,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -74,14 +74,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -98,9 +98,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "draft-pr-cleanup.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -115,15 +115,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, close_pull_request, add_labels, missing_tool, missing_data, noop
@@ -157,6 +157,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -172,9 +173,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -190,10 +191,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -212,11 +213,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -241,10 +242,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: draftprcleanup
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -266,13 +268,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -295,16 +301,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -313,216 +319,32 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":20},"add_labels":{"max":20},"close_pull_request":{"max":10,"target":"*"},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Close a pull request WITHOUT merging, adding a closing comment. You can and should always add a comment when closing a PR to explain the action or provide context. Use this for PRs that should be abandoned, superseded, or closed for other reasons. The closing comment should explain why the PR is being closed. This does NOT merge the changes. If the PR is already closed, a comment will still be posted. CONSTRAINTS: Maximum 10 pull request(s) can be closed. Target: *.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Closing comment explaining why the PR is being closed without merging (e.g., superseded by another PR, no longer needed, approach rejected).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "pull_request_number": {
- "description": "Pull request number to close. This is the numeric ID from the GitHub URL (e.g., 432 in github.com/owner/repo/pull/432). If omitted, closes the PR that triggered this workflow (requires a pull_request event trigger).",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "close_pull_request"
- },
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 20 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Add labels to an existing GitHub issue or pull request for categorization and filtering. Labels must already exist in the repository. For creating new issues with labels, use create_issue with the labels property instead. CONSTRAINTS: Maximum 20 label(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). If omitted, adds labels to the issue or PR that triggered this workflow. Only works for issue or pull_request event triggers. For schedule, workflow_dispatch, or other triggers, item_number is required — omitting it will silently skip the label operation.",
- "type": "number"
- },
- "labels": {
- "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "add_labels"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 20 comment(s) can be added.",
+ "add_labels": " CONSTRAINTS: Maximum 20 label(s) can be added.",
+ "close_pull_request": " CONSTRAINTS: Maximum 10 pull request(s) can be closed. Target: *."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -638,6 +460,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -662,8 +485,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -674,7 +497,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -682,7 +505,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -700,10 +524,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -711,10 +535,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "pull_requests,repos"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -722,6 +551,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -739,7 +575,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -764,7 +601,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq *)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -793,7 +630,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -831,15 +668,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -848,7 +685,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -865,9 +702,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -876,18 +713,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -962,9 +799,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -987,7 +824,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1015,9 +852,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1062,6 +899,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-draft-pr-cleanup"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1077,7 +916,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1101,9 +940,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1114,9 +953,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1136,9 +975,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1153,9 +992,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1171,6 +1010,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/draft-pr-cleanup"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"runStarted\":\"🧹 Starting draft PR cleanup... [{workflow_name}]({run_url}) is reviewing draft PRs for staleness\",\"runSuccess\":\"✅ Draft PR cleanup complete! [{workflow_name}]({run_url}) has reviewed and processed stale drafts.\",\"runFailure\":\"❌ Draft PR cleanup failed! [{workflow_name}]({run_url}) {status}. Some draft PRs may not be processed.\"}"
GH_AW_WORKFLOW_ID: "draft-pr-cleanup"
GH_AW_WORKFLOW_NAME: "Draft PR Cleanup"
@@ -1194,7 +1034,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1220,9 +1060,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml
index 7ec825fe0ef..e3fdaea8cc4 100644
--- a/.github/workflows/duplicate-code-detector.lock.yml
+++ b/.github/workflows/duplicate-code-detector.lock.yml
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,18 +80,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate CODEX_API_KEY or OPENAI_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
@@ -110,9 +110,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "duplicate-code-detector.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -128,15 +128,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -170,6 +170,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -193,9 +194,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -212,10 +213,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -235,11 +236,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -264,10 +265,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: duplicatecodedetector
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -288,13 +290,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -317,9 +323,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -329,7 +335,7 @@ jobs:
- name: Install Codex
run: npm install -g @openai/codex@latest
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -338,167 +344,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Assignees [\"copilot\"] will be automatically assigned.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Assignees [\"copilot\"] will be automatically assigned."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -592,6 +461,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -616,8 +486,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -628,7 +498,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -636,7 +506,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -654,7 +525,7 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="codex"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
cat > /tmp/gh-aw/mcp-config/config.toml << GH_AW_MCP_CONFIG_EOF
[history]
@@ -679,6 +550,11 @@ jobs:
[mcp_servers.safeoutputs.headers]
Authorization = "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ [mcp_servers.safeoutputs."guard-policies"]
+
+ [mcp_servers.safeoutputs."guard-policies".write-sink]
+ accept = ["*"]
+
[mcp_servers.serena]
container = "ghcr.io/github/serena-mcp-server:latest"
args = [
@@ -694,20 +570,30 @@ jobs:
"${GITHUB_WORKSPACE}"
]
mounts = ["${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw"]
+
+ [mcp_servers.serena."guard-policies"]
+
+ [mcp_servers.serena."guard-policies".write-sink]
+ accept = ["*"]
GH_AW_MCP_CONFIG_EOF
# Generate JSON config for MCP gateway
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -715,6 +601,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -731,7 +624,14 @@ jobs:
"--project",
"\${GITHUB_WORKSPACE}"
],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -748,13 +648,14 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Codex
run: |
set -o pipefail
mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" && codex ${GH_AW_MODEL_AGENT_CODEX:+-c model="$GH_AW_MODEL_AGENT_CODEX" }exec -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
@@ -793,15 +694,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'CODEX_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY'
@@ -812,7 +713,7 @@ jobs:
SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -829,9 +730,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -840,18 +741,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_codex_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_codex_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -926,9 +827,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -941,7 +842,7 @@ jobs:
set -o pipefail
mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,host.docker.internal,openai.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,host.docker.internal,openai.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" && codex ${GH_AW_MODEL_DETECTION_CODEX:+-c model="$GH_AW_MODEL_DETECTION_CODEX" }exec -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
@@ -965,9 +866,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1010,6 +911,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-duplicate-code-detector"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1025,7 +928,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1049,9 +952,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1062,9 +965,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1083,9 +986,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1100,9 +1003,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1116,6 +1019,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/duplicate-code-detector"
GH_AW_ENGINE_ID: "codex"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "duplicate-code-detector"
GH_AW_WORKFLOW_NAME: "Duplicate Code Detector"
outputs:
@@ -1138,7 +1042,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1165,9 +1069,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Assign Copilot to created issues
if: steps.process_safe_outputs.outputs.issues_to_assign_copilot != ''
@@ -1177,9 +1081,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/assign_copilot_to_created_issues.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/assign_copilot_to_created_issues.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/example-permissions-warning.lock.yml b/.github/workflows/example-permissions-warning.lock.yml
index eab3175d2f8..68141be7c30 100644
--- a/.github/workflows/example-permissions-warning.lock.yml
+++ b/.github/workflows/example-permissions-warning.lock.yml
@@ -57,7 +57,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -72,18 +72,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "false"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -101,9 +101,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "example-permissions-warning.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -117,14 +117,14 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
cat << 'GH_AW_PROMPT_EOF'
The following GitHub context information is available for this workflow:
@@ -155,6 +155,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -168,9 +169,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -186,10 +187,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -208,11 +209,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -231,6 +232,7 @@ jobs:
issues: read
pull-requests: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: examplepermissionswarning
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -247,13 +249,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -276,16 +282,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -294,14 +300,15 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0
- name: Start MCP Gateway
id: start-mcp-gateway
env:
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -319,10 +326,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -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 -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -330,10 +337,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
}
},
@@ -351,7 +363,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -360,7 +373,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -387,7 +400,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -425,15 +438,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -443,7 +456,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Parse agent logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -451,18 +464,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml
index e4770ddae6e..24dce5c8025 100644
--- a/.github/workflows/example-workflow-analyzer.lock.yml
+++ b/.github/workflows/example-workflow-analyzer.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "example-workflow-analyzer.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,15 +125,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -167,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -183,9 +185,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -201,10 +203,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -223,11 +225,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -253,10 +255,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: exampleworkflowanalyzer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -277,7 +280,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -313,7 +316,11 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -336,9 +343,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -346,7 +353,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -357,10 +364,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -375,158 +382,36 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[workflow-analysis] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[workflow-analysis] \". Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -613,6 +498,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -637,8 +523,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -649,7 +535,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -657,7 +543,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -676,9 +563,9 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -690,16 +577,28 @@ jobs:
"GITHUB_TOKEN": "$GITHUB_TOKEN",
"GITHUB_ACTOR": "$GITHUB_ACTOR",
"GITHUB_REPOSITORY": "$GITHUB_REPOSITORY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,actions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -707,6 +606,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -724,7 +630,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -800,7 +707,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools Bash,BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -844,15 +751,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -862,7 +769,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -879,9 +786,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -890,18 +797,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -974,9 +881,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1009,7 +916,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1037,9 +944,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1083,6 +990,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-example-workflow-analyzer"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1098,7 +1007,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1122,9 +1031,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1135,9 +1044,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1158,9 +1067,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1175,9 +1084,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1192,6 +1101,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/example-workflow-analyzer"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "example-workflow-analyzer"
GH_AW_WORKFLOW_NAME: "Weekly Workflow Analysis"
outputs:
@@ -1212,7 +1122,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1238,9 +1148,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/firewall-escape.lock.yml b/.github/workflows/firewall-escape.lock.yml
index 8bea34fba32..6d065feb8a0 100644
--- a/.github/workflows/firewall-escape.lock.yml
+++ b/.github/workflows/firewall-escape.lock.yml
@@ -72,7 +72,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -87,14 +87,14 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","node"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -111,18 +111,18 @@ jobs:
GH_AW_WORKFLOW_FILE: "firewall-escape.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/compute_text.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -138,17 +138,17 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -182,6 +182,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -196,9 +197,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -224,10 +225,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -256,11 +257,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -286,10 +287,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: firewallescape
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -311,16 +313,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -337,7 +343,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -360,16 +366,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -378,167 +384,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":50,"max_file_size":524288,"max_patch_size":10240}]}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[Firewall Escape] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[Firewall Escape] \". Discussions will be created in category \"audits\"."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -625,6 +494,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -649,8 +519,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -661,7 +531,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -669,7 +539,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -687,10 +558,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -698,10 +569,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,discussions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -709,6 +585,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -726,7 +609,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -735,7 +619,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -764,7 +648,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -802,15 +686,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -819,7 +703,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -836,9 +720,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -847,18 +731,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -948,9 +832,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -973,7 +857,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1001,9 +885,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1049,6 +933,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-firewall-escape"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1064,7 +950,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1089,9 +975,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1103,9 +989,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1131,9 +1017,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1149,9 +1035,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
post-issue:
@@ -1204,7 +1090,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1213,9 +1099,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
push_repo_memory:
@@ -1227,6 +1113,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1242,7 +1130,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1284,9 +1172,9 @@ jobs:
ALLOWED_EXTENSIONS: '[]'
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1301,6 +1189,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/firewall-escape"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "firewall-escape"
GH_AW_WORKFLOW_ID: "firewall-escape"
GH_AW_WORKFLOW_NAME: "The Great Escapi"
@@ -1322,7 +1211,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1348,9 +1237,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1367,6 +1256,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: firewallescape
steps:
- name: Checkout actions folder
@@ -1379,7 +1269,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/firewall.lock.yml b/.github/workflows/firewall.lock.yml
index 0e560d6dc12..65ec2228bac 100644
--- a/.github/workflows/firewall.lock.yml
+++ b/.github/workflows/firewall.lock.yml
@@ -57,7 +57,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -72,18 +72,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","node"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -101,9 +101,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "firewall.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -117,14 +117,14 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
cat << 'GH_AW_PROMPT_EOF'
The following GitHub context information is available for this workflow:
@@ -155,6 +155,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -170,9 +171,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -188,10 +189,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -210,11 +211,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -233,6 +234,7 @@ jobs:
issues: read
pull-requests: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: firewall
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -249,13 +251,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -278,16 +284,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -296,14 +302,15 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0
- name: Start MCP Gateway
id: start-mcp-gateway
env:
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -321,10 +328,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -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 -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -332,10 +339,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
}
},
@@ -353,7 +365,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -362,7 +375,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -389,7 +402,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -427,15 +440,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -445,7 +458,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Parse agent logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -453,18 +466,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
diff --git a/.github/workflows/functional-pragmatist.lock.yml b/.github/workflows/functional-pragmatist.lock.yml
index fd9cee2c672..55c55622a47 100644
--- a/.github/workflows/functional-pragmatist.lock.yml
+++ b/.github/workflows/functional-pragmatist.lock.yml
@@ -63,7 +63,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -78,18 +78,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github","go"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -107,9 +107,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "functional-pragmatist.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -124,20 +124,20 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -169,6 +169,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -187,9 +188,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -205,10 +206,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -227,11 +228,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -256,10 +257,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: functionalpragmatist
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -281,13 +283,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -310,16 +316,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -328,167 +334,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request":{"expires":24,"max":1,"reviewers":["copilot"],"title_prefix":"[fp-enhancer] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[fp-enhancer] \". Labels [\"refactoring\" \"functional\" \"immutability\" \"code-quality\"] will be automatically added. Reviewers [\"copilot\"] will be assigned.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[fp-enhancer] \". Labels [\"refactoring\" \"functional\" \"immutability\" \"code-quality\"] will be automatically added. Reviewers [\"copilot\"] will be assigned."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -585,6 +454,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -609,8 +479,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -621,7 +491,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -629,7 +499,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -647,10 +518,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -658,10 +529,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -669,6 +545,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -686,7 +569,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -695,7 +579,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -723,7 +607,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -761,15 +645,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -779,7 +663,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -791,14 +675,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -807,18 +691,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -894,9 +778,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -919,7 +803,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -946,9 +830,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -992,6 +876,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-functional-pragmatist"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1007,7 +893,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1032,9 +918,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1046,9 +932,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1071,9 +957,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1089,9 +975,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1104,9 +990,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
safe_outputs:
@@ -1123,6 +1009,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/functional-pragmatist"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "functional-pragmatist"
GH_AW_WORKFLOW_ID: "functional-pragmatist"
GH_AW_WORKFLOW_NAME: "Functional Pragmatist"
@@ -1146,7 +1033,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1193,7 +1080,7 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"expires\":24,\"labels\":[\"refactoring\",\"functional\",\"immutability\",\"code-quality\"],\"max\":1,\"max_patch_size\":1024,\"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\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"reviewers\":[\"copilot\"],\"title_prefix\":\"[fp-enhancer] \"},\"missing_data\":{},\"missing_tool\":{}}"
@@ -1201,9 +1088,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/github-mcp-structural-analysis.lock.yml b/.github/workflows/github-mcp-structural-analysis.lock.yml
index a49758bcc40..d62a6f70f30 100644
--- a/.github/workflows/github-mcp-structural-analysis.lock.yml
+++ b/.github/workflows/github-mcp-structural-analysis.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "github-mcp-structural-analysis.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,16 +125,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, upload_asset, missing_tool, missing_data, noop
@@ -170,6 +170,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -191,9 +192,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -212,10 +213,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -237,11 +238,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -269,10 +270,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: githubmcpstructuralanalysis
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -293,13 +295,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup Python environment
run: "# Create working directory for Python scripts\nmkdir -p /tmp/gh-aw/python\nmkdir -p /tmp/gh-aw/python/data\nmkdir -p /tmp/gh-aw/python/charts\nmkdir -p /tmp/gh-aw/python/artifacts\n\necho \"Python environment setup complete\"\necho \"Working directory: /tmp/gh-aw/python\"\necho \"Data directory: /tmp/gh-aw/python/data\"\necho \"Charts directory: /tmp/gh-aw/python/charts\"\necho \"Artifacts directory: /tmp/gh-aw/python/artifacts\"\n"
- name: Install Python scientific libraries
@@ -325,7 +331,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -355,9 +361,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -365,7 +371,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -376,177 +382,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[mcp-analysis] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[mcp-analysis] \". Discussions will be created in category \"audits\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -642,6 +502,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -666,8 +527,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -678,7 +539,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -689,7 +550,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -707,19 +569,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "all"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -727,6 +594,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -744,7 +618,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -824,7 +699,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash,BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -871,15 +746,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -889,7 +764,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -906,9 +781,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -917,18 +792,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1016,9 +891,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1051,7 +926,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1079,9 +954,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1127,6 +1002,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-github-mcp-structural-analysis"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1142,7 +1019,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1166,9 +1043,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1179,9 +1056,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1202,9 +1079,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1219,9 +1096,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1236,6 +1113,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/github-mcp-structural-analysis"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "github-mcp-structural-analysis"
GH_AW_WORKFLOW_NAME: "GitHub MCP Structural Analysis"
outputs:
@@ -1256,7 +1134,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1282,9 +1160,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1301,6 +1179,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: githubmcpstructuralanalysis
steps:
- name: Checkout actions folder
@@ -1313,7 +1192,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1358,7 +1237,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1413,8 +1292,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml
index 1eba3bc8926..cc77b7aba09 100644
--- a/.github/workflows/github-mcp-tools-report.lock.yml
+++ b/.github/workflows/github-mcp-tools-report.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "github-mcp-tools-report.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,21 +125,21 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -171,6 +171,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -188,9 +189,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -209,10 +210,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -234,11 +235,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -266,10 +267,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: githubmcptoolsreport
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -290,16 +292,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -329,9 +335,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -339,7 +345,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -350,201 +356,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":168,"max":1},"create_pull_request":{"expires":48,"max":1,"reviewers":["copilot"],"title_prefix":"[mcp-tools] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[mcp-tools] \". Labels [\"documentation\" \"automation\"] will be automatically added. Reviewers [\"copilot\"] will be assigned.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[mcp-tools] \". Labels [\"documentation\" \"automation\"] will be automatically added. Reviewers [\"copilot\"] will be assigned."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -667,6 +503,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -691,8 +528,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -703,7 +540,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -711,7 +548,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -729,9 +567,9 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -739,9 +577,14 @@ jobs:
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer $GITHUB_MCP_SERVER_TOKEN",
- "X-MCP-Lockdown": "$([ "$GITHUB_MCP_LOCKDOWN" = "1" ] && echo true || echo false)",
"X-MCP-Readonly": "true",
"X-MCP-Toolsets": "all"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -749,6 +592,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -766,7 +616,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -846,7 +697,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash,BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -890,15 +741,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -908,7 +759,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -925,9 +776,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -936,18 +787,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1027,9 +878,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1062,7 +913,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1090,9 +941,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1138,6 +989,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-github-mcp-tools-report"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1153,7 +1006,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1177,9 +1030,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1190,9 +1043,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1215,9 +1068,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1232,9 +1085,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1246,9 +1099,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
safe_outputs:
@@ -1266,6 +1119,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/github-mcp-tools-report"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "github-mcp-tools-report"
GH_AW_WORKFLOW_NAME: "GitHub MCP Remote Server Tools Report Generator"
outputs:
@@ -1288,7 +1142,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1343,9 +1197,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1362,6 +1216,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: githubmcptoolsreport
steps:
- name: Checkout actions folder
@@ -1374,7 +1229,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/github-remote-mcp-auth-test.lock.yml b/.github/workflows/github-remote-mcp-auth-test.lock.yml
index 05f761aa9ec..835f3ea9d36 100644
--- a/.github/workflows/github-remote-mcp-auth-test.lock.yml
+++ b/.github/workflows/github-remote-mcp-auth-test.lock.yml
@@ -60,7 +60,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -75,18 +75,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -104,9 +104,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "github-remote-mcp-auth-test.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -122,15 +122,15 @@ jobs:
GH_AW_GITHUB_WORKFLOW: ${{ github.workflow }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -164,6 +164,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -180,9 +181,9 @@ jobs:
GH_AW_GITHUB_WORKFLOW: ${{ github.workflow }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -199,10 +200,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -222,11 +223,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -251,10 +252,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: githubremotemcpauthtest
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -276,13 +278,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -305,16 +311,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -323,152 +329,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[auth-test] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[auth-test] \". Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -555,6 +439,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -579,8 +464,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -591,7 +476,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -599,7 +484,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -618,10 +504,10 @@ jobs:
export GH_AW_ENGINE="copilot"
export GITHUB_PERSONAL_ACCESS_TOKEN="$GITHUB_MCP_SERVER_TOKEN"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_PERSONAL_ACCESS_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_PERSONAL_ACCESS_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -629,7 +515,6 @@ jobs:
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer \${GITHUB_PERSONAL_ACCESS_TOKEN}",
- "X-MCP-Lockdown": "$([ "$GITHUB_MCP_LOCKDOWN" = "1" ] && echo true || echo false)",
"X-MCP-Readonly": "true",
"X-MCP-Toolsets": "repos,issues,discussions"
},
@@ -641,6 +526,12 @@ jobs:
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_HOST": "\${GITHUB_SERVER_URL}"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -648,6 +539,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -665,7 +563,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -674,7 +573,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -702,7 +601,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -740,15 +639,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -758,7 +657,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -775,9 +674,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -786,18 +685,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -872,9 +771,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -897,7 +796,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -924,9 +823,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -970,6 +869,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-github-remote-mcp-auth-test"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -985,7 +886,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1009,9 +910,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1022,9 +923,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1046,9 +947,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1063,9 +964,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1081,6 +982,7 @@ jobs:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/github-remote-mcp-auth-test"
GH_AW_ENGINE_ID: "copilot"
GH_AW_ENGINE_MODEL: "gpt-5.1-codex-mini"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "github-remote-mcp-auth-test"
GH_AW_WORKFLOW_NAME: "GitHub Remote MCP Authentication Test"
outputs:
@@ -1101,7 +1003,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1127,9 +1029,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/glossary-maintainer.lock.yml b/.github/workflows/glossary-maintainer.lock.yml
index 1db27c54190..634e9cfdeab 100644
--- a/.github/workflows/glossary-maintainer.lock.yml
+++ b/.github/workflows/glossary-maintainer.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,18 +81,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -110,9 +110,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "glossary-maintainer.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -127,22 +127,22 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -174,6 +174,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -200,9 +201,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -227,10 +228,10 @@ jobs:
GH_AW_WIKI_NOTE: "\n\n> **GitHub Wiki**: This memory is backed by the GitHub Wiki for this repository. Files use GitHub Wiki Markdown syntax. Follow GitHub Wiki conventions when creating or editing pages (e.g., use standard Markdown headers, use `[[Page Name]]` syntax for internal wiki links, name page files with spaces replaced by hyphens or use the wiki page title as the filename)."
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -258,11 +259,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -288,10 +289,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: glossarymaintainer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -313,7 +315,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -325,9 +327,9 @@ jobs:
GH_AW_AGENT_IMPORT_SPEC: "../agents/technical-doc-writer.agent.md"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/merge_remote_agent_github_folder.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/merge_remote_agent_github_folder.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -335,7 +337,11 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Install QMD
run: npm install -g @tobilu/qmd
- name: Restore QMD index cache
@@ -350,7 +356,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -367,7 +373,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}.wiki
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: false
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -390,16 +396,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -408,182 +414,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request":{"expires":48,"max":1,"title_prefix":"[docs] "},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[docs] \". Labels [\"documentation\" \"glossary\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[docs] \". Labels [\"documentation\" \"glossary\"] will be automatically added."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -680,6 +534,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -704,8 +559,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -716,16 +571,16 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Setup MCP Scripts Config
run: |
- mkdir -p /opt/gh-aw/mcp-scripts/logs
- cat > /opt/gh-aw/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
+ mkdir -p ${GH_AW_HOME}/mcp-scripts/logs
+ cat > ${GH_AW_HOME}/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
{
"serverName": "mcpscripts",
"version": "1.0.0",
- "logDir": "/opt/gh-aw/mcp-scripts/logs",
+ "logDir": "${GH_AW_HOME}/mcp-scripts/logs",
"tools": [
{
"name": "qmd-query",
@@ -753,7 +608,7 @@ jobs:
]
}
GH_AW_MCP_SCRIPTS_TOOLS_EOF
- cat > /opt/gh-aw/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
const path = require("path");
const { startHttpServer } = require("./mcp_scripts_mcp_server_http.cjs");
const configPath = path.join(__dirname, "tools.json");
@@ -762,17 +617,17 @@ jobs:
startHttpServer(configPath, {
port: port,
stateless: true,
- logDir: "/opt/gh-aw/mcp-scripts/logs"
+ logDir: process.env.GH_AW_HOME + "/mcp-scripts/logs"
}).catch(error => {
console.error("Failed to start mcp-scripts HTTP server:", error);
process.exit(1);
});
GH_AW_MCP_SCRIPTS_SERVER_EOF
- chmod +x /opt/gh-aw/mcp-scripts/mcp-server.cjs
+ chmod +x ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs
- name: Setup MCP Scripts Tool Files
run: |
- cat > /opt/gh-aw/mcp-scripts/qmd-query.sh << 'GH_AW_MCP_SCRIPTS_SH_QMD-QUERY_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/qmd-query.sh << 'GH_AW_MCP_SCRIPTS_SH_QMD-QUERY_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: qmd-query
# Find relevant file paths in project documentation using vector similarity search. Returns file paths and scores.
@@ -784,7 +639,7 @@ jobs:
GH_AW_MCP_SCRIPTS_SH_QMD-QUERY_EOF
- chmod +x /opt/gh-aw/mcp-scripts/qmd-query.sh
+ chmod +x ${GH_AW_HOME}/mcp-scripts/qmd-query.sh
- name: Generate MCP Scripts Server Config
id: mcp-scripts-config
@@ -816,7 +671,7 @@ jobs:
export GH_AW_MCP_SCRIPTS_PORT
export GH_AW_MCP_SCRIPTS_API_KEY
- bash /opt/gh-aw/actions/start_mcp_scripts_server.sh
+ bash ${GH_AW_HOME}/actions/start_mcp_scripts_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -826,7 +681,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -844,10 +700,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -855,10 +711,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"mcpscripts": {
@@ -866,6 +727,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_MCP_SCRIPTS_PORT",
"headers": {
"Authorization": "\${GH_AW_MCP_SCRIPTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"safeoutputs": {
@@ -873,6 +741,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -881,7 +756,14 @@ jobs:
"args": ["--network", "host"],
"entrypoint": "serena",
"entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -898,7 +780,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -936,7 +819,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --agent technical-doc-writer --allow-tool github --allow-tool mcpscripts --allow-tool safeoutputs --allow-tool serena --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find docs -name '\''\'\'''\''*.md'\''\'\'''\'')'\'' --allow-tool '\''shell(git add:*)'\'' --allow-tool '\''shell(git branch:*)'\'' --allow-tool '\''shell(git checkout:*)'\'' --allow-tool '\''shell(git commit:*)'\'' --allow-tool '\''shell(git log --since='\''\'\'''\''24 hours ago'\''\'\'''\'' --oneline)'\'' --allow-tool '\''shell(git log --since='\''\'\'''\''7 days ago'\''\'\'''\'' --oneline)'\'' --allow-tool '\''shell(git merge:*)'\'' --allow-tool '\''shell(git rm:*)'\'' --allow-tool '\''shell(git status)'\'' --allow-tool '\''shell(git switch:*)'\'' --allow-tool '\''shell(grep -r '\''\'\'''\''*'\''\'\'''\'' docs)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -964,7 +847,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -1002,15 +885,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -1020,7 +903,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1032,14 +915,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1048,27 +931,27 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Scripts logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_scripts_logs.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_scripts_logs.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1160,9 +1043,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1185,7 +1068,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1212,9 +1095,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1260,6 +1143,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-glossary-maintainer"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1275,7 +1160,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1299,9 +1184,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1312,9 +1197,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1340,9 +1225,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1357,9 +1242,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1371,9 +1256,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
push_repo_memory:
@@ -1385,6 +1270,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1400,7 +1287,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1443,9 +1330,9 @@ jobs:
ALLOWED_EXTENSIONS: '[]'
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1462,6 +1349,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/glossary-maintainer"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "glossary-maintainer"
GH_AW_WORKFLOW_NAME: "Glossary Maintainer"
outputs:
@@ -1484,7 +1372,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1531,7 +1419,7 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"draft\":false,\"expires\":48,\"labels\":[\"documentation\",\"glossary\"],\"max\":1,\"max_patch_size\":1024,\"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\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"title_prefix\":\"[docs] \"},\"missing_data\":{},\"missing_tool\":{}}"
@@ -1539,9 +1427,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1558,6 +1446,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: glossarymaintainer
steps:
- name: Checkout actions folder
@@ -1570,7 +1459,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/go-fan.lock.yml b/.github/workflows/go-fan.lock.yml
index 1a448b4ee18..e64ed0ec474 100644
--- a/.github/workflows/go-fan.lock.yml
+++ b/.github/workflows/go-fan.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github","go"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "go-fan.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,16 +125,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -168,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -190,9 +191,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -211,10 +212,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -236,11 +237,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -266,10 +267,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: gofan
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -290,16 +292,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -329,9 +335,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -339,7 +345,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -350,152 +356,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[go-fan] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[go-fan] \". Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -582,6 +466,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -606,8 +491,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -618,7 +503,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -626,7 +511,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -644,19 +530,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -664,6 +555,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -680,7 +578,14 @@ jobs:
"--project",
"\${GITHUB_WORKSPACE}"
],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -697,7 +602,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -795,7 +701,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(cat go.mod),Bash(cat go.sum),Bash(cat scratchpad/mods/*),Bash(cat),Bash(date),Bash(echo),Bash(find pkg -name '\''\'\'''\''*.go'\''\'\'''\''),Bash(find scratchpad/mods/ -maxdepth 1 -ls),Bash(go list -m all),Bash(grep -r '\''\'\'''\''import'\''\'\'''\'' --include='\''\'\'''\''*.go'\''\'\'''\''),Bash(grep),Bash(head),Bash(ls),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -839,15 +745,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -857,7 +763,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -869,14 +775,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -885,18 +791,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -975,9 +881,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1010,7 +916,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1038,9 +944,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1085,6 +991,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-go-fan"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1100,7 +1008,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1125,9 +1033,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1139,9 +1047,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1163,9 +1071,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1181,9 +1089,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1198,6 +1106,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/go-fan"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "go-fan-daily"
GH_AW_WORKFLOW_ID: "go-fan"
GH_AW_WORKFLOW_NAME: "Go Fan"
@@ -1219,7 +1128,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1238,16 +1147,16 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[go-fan] \"},\"missing_data\":{},\"missing_tool\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1264,6 +1173,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: gofan
steps:
- name: Checkout actions folder
@@ -1276,7 +1186,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/go-logger.lock.yml b/.github/workflows/go-logger.lock.yml
index 020e37413cc..c6a2afb548e 100644
--- a/.github/workflows/go-logger.lock.yml
+++ b/.github/workflows/go-logger.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","go"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "go-logger.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,21 +125,21 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -171,6 +171,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -187,9 +188,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -208,10 +209,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -233,11 +234,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -262,10 +263,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: gologger
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -286,7 +288,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -299,7 +301,11 @@ jobs:
cache-dependency-path: 'actions/setup/js/package-lock.json'
package-manager-cache: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup Go
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
@@ -311,7 +317,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -341,9 +347,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -351,7 +357,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -362,167 +368,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request":{"expires":48,"max":1,"title_prefix":"[log] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[log] \". Labels [\"enhancement\" \"automation\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[log] \". Labels [\"enhancement\" \"automation\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -619,6 +488,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -643,8 +513,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -655,16 +525,16 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Setup MCP Scripts Config
run: |
- mkdir -p /opt/gh-aw/mcp-scripts/logs
- cat > /opt/gh-aw/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
+ mkdir -p ${GH_AW_HOME}/mcp-scripts/logs
+ cat > ${GH_AW_HOME}/mcp-scripts/tools.json << 'GH_AW_MCP_SCRIPTS_TOOLS_EOF'
{
"serverName": "mcpscripts",
"version": "1.0.0",
- "logDir": "/opt/gh-aw/mcp-scripts/logs",
+ "logDir": "${GH_AW_HOME}/mcp-scripts/logs",
"tools": [
{
"name": "go",
@@ -705,7 +575,7 @@ jobs:
]
}
GH_AW_MCP_SCRIPTS_TOOLS_EOF
- cat > /opt/gh-aw/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs << 'GH_AW_MCP_SCRIPTS_SERVER_EOF'
const path = require("path");
const { startHttpServer } = require("./mcp_scripts_mcp_server_http.cjs");
const configPath = path.join(__dirname, "tools.json");
@@ -714,17 +584,17 @@ jobs:
startHttpServer(configPath, {
port: port,
stateless: true,
- logDir: "/opt/gh-aw/mcp-scripts/logs"
+ logDir: process.env.GH_AW_HOME + "/mcp-scripts/logs"
}).catch(error => {
console.error("Failed to start mcp-scripts HTTP server:", error);
process.exit(1);
});
GH_AW_MCP_SCRIPTS_SERVER_EOF
- chmod +x /opt/gh-aw/mcp-scripts/mcp-server.cjs
+ chmod +x ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs
- name: Setup MCP Scripts Tool Files
run: |
- cat > /opt/gh-aw/mcp-scripts/go.sh << 'GH_AW_MCP_SCRIPTS_SH_GO_EOF'
+ cat > ${GH_AW_HOME}/mcp-scripts/go.sh << 'GH_AW_MCP_SCRIPTS_SH_GO_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: go
# Execute any Go command. This tool is accessible as 'mcpscripts-go'. Provide the full command after 'go' (e.g., args: 'test ./...'). The tool will run: go . Use single quotes ' for complex args to avoid shell interpretation issues.
@@ -736,8 +606,8 @@ jobs:
GH_AW_MCP_SCRIPTS_SH_GO_EOF
- chmod +x /opt/gh-aw/mcp-scripts/go.sh
- cat > /opt/gh-aw/mcp-scripts/make.sh << 'GH_AW_MCP_SCRIPTS_SH_MAKE_EOF'
+ chmod +x ${GH_AW_HOME}/mcp-scripts/go.sh
+ cat > ${GH_AW_HOME}/mcp-scripts/make.sh << 'GH_AW_MCP_SCRIPTS_SH_MAKE_EOF'
#!/bin/bash
# Auto-generated mcp-script tool: make
# Execute any Make target. This tool is accessible as 'mcpscripts-make'. Provide the target name(s) (e.g., args: 'build'). The tool will run: make . Use single quotes ' for complex args to avoid shell interpretation issues.
@@ -748,7 +618,7 @@ jobs:
make $INPUT_ARGS
GH_AW_MCP_SCRIPTS_SH_MAKE_EOF
- chmod +x /opt/gh-aw/mcp-scripts/make.sh
+ chmod +x ${GH_AW_HOME}/mcp-scripts/make.sh
- name: Generate MCP Scripts Server Config
id: mcp-scripts-config
@@ -780,7 +650,7 @@ jobs:
export GH_AW_MCP_SCRIPTS_PORT
export GH_AW_MCP_SCRIPTS_API_KEY
- bash /opt/gh-aw/actions/start_mcp_scripts_server.sh
+ bash ${GH_AW_HOME}/actions/start_mcp_scripts_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -790,7 +660,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -808,19 +679,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"mcpscripts": {
@@ -828,6 +704,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_MCP_SCRIPTS_PORT",
"headers": {
"Authorization": "$GH_AW_MCP_SCRIPTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"safeoutputs": {
@@ -835,6 +718,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -852,7 +742,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -960,7 +851,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(./gh-aw compile *),Bash(cat),Bash(date),Bash(echo),Bash(find pkg -name '\''\'\'''\''*.go'\''\'\'''\'' -type f ! -name '\''\'\'''\''*_test.go'\''\'\'''\''),Bash(git add:*),Bash(git branch:*),Bash(git checkout:*),Bash(git commit:*),Bash(git merge:*),Bash(git rm:*),Bash(git status),Bash(git switch:*),Bash(git),Bash(grep -n '\''\'\'''\''func '\''\'\'''\'' pkg/*.go),Bash(grep -r '\''\'\'''\''var log = logger.New'\''\'\'''\'' pkg --include='\''\'\'''\''*.go'\''\'\'''\''),Bash(grep),Bash(head -n * pkg/**/*.go),Bash(head),Bash(ls),Bash(make build),Bash(make recompile),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc -l pkg/**/*.go),Bash(wc),Bash(yq),BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1004,15 +895,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -1022,7 +913,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1039,9 +930,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1050,27 +941,27 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Scripts logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_scripts_logs.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_scripts_logs.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1151,9 +1042,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1186,7 +1077,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1214,9 +1105,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1261,6 +1152,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-go-logger"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1276,7 +1169,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1300,9 +1193,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1313,9 +1206,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1336,9 +1229,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1353,9 +1246,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1367,9 +1260,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
safe_outputs:
@@ -1386,6 +1279,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/go-logger"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "go-logger"
GH_AW_WORKFLOW_NAME: "Go Logger Enhancement"
outputs:
@@ -1408,7 +1302,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1463,9 +1357,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1482,6 +1376,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: gologger
steps:
- name: Checkout actions folder
@@ -1494,7 +1389,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml
index ebd2130943b..865d39007f2 100644
--- a/.github/workflows/go-pattern-detector.lock.yml
+++ b/.github/workflows/go-pattern-detector.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "go-pattern-detector.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -126,15 +126,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -168,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -190,9 +191,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -209,10 +210,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -232,11 +233,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -264,10 +265,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: gopatterndetector
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -288,13 +290,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -317,9 +323,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -327,7 +333,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -338,167 +344,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcp/ast-grep:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcp/ast-grep:latest node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":48,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[ast-grep] \". Labels [\"code-quality\" \"ast-grep\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[ast-grep] \". Labels [\"code-quality\" \"ast-grep\" \"cookie\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -592,6 +461,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -616,8 +486,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -628,7 +498,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -636,7 +506,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -654,9 +525,9 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"ast-grep": {
@@ -664,16 +535,28 @@ jobs:
"container": "mcp/ast-grep:latest",
"tools": [
"*"
- ]
+ ],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -681,6 +564,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -698,7 +588,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -775,7 +666,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools Bash,BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__ast-grep,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -819,15 +710,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -837,7 +728,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -854,9 +745,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -865,18 +756,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -949,9 +840,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -984,7 +875,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1012,9 +903,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1091,6 +982,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-go-pattern-detector"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1106,7 +999,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1130,9 +1023,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1143,9 +1036,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1164,9 +1057,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1181,9 +1074,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1197,6 +1090,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/go-pattern-detector"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "go-pattern-detector"
GH_AW_WORKFLOW_NAME: "Go Pattern Detector"
outputs:
@@ -1219,7 +1113,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1245,9 +1139,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/gpclean.lock.yml b/.github/workflows/gpclean.lock.yml
index 29c74888e13..5f647300ad5 100644
--- a/.github/workflows/gpclean.lock.yml
+++ b/.github/workflows/gpclean.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["pkg.go.dev","proxy.golang.org","sum.golang.org","go.googlesource.com","api.github.com","github.com"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "false"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "gpclean.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,16 +125,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -168,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -185,9 +186,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -206,10 +207,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -231,11 +232,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -260,10 +261,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: gpclean
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -285,13 +287,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -300,7 +306,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -330,16 +336,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -348,167 +354,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":48,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[gpl-dependency]\". Labels [\"dependency-cleaner\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[gpl-dependency]\". Labels [\"dependency-cleaner\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -602,6 +471,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -626,8 +496,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -638,7 +508,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -646,7 +516,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -664,10 +535,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -675,10 +546,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -686,6 +562,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -703,7 +586,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -712,7 +596,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,go.googlesource.com,host.docker.internal,pkg.go.dev,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,sum.golang.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,go.googlesource.com,host.docker.internal,pkg.go.dev,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,sum.golang.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -740,7 +624,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -778,15 +662,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -796,7 +680,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -813,9 +697,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -824,18 +708,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -916,9 +800,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -941,7 +825,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -968,9 +852,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1014,6 +898,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-gpclean"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1029,7 +915,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1053,9 +939,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1066,9 +952,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1088,9 +974,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1105,9 +991,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1121,6 +1007,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/gpclean"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "gpclean"
GH_AW_WORKFLOW_NAME: "GPL Dependency Cleaner (gpclean)"
outputs:
@@ -1143,7 +1030,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1169,9 +1056,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1188,6 +1075,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: gpclean
steps:
- name: Checkout actions folder
@@ -1200,7 +1088,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml
index e8338430985..fe925a67298 100644
--- a/.github/workflows/grumpy-reviewer.lock.yml
+++ b/.github/workflows/grumpy-reviewer.lock.yml
@@ -58,8 +58,9 @@ jobs:
pull-requests: write
outputs:
body: ${{ steps.sanitized.outputs.body }}
- comment_id: ""
- comment_repo: ""
+ comment_id: ${{ steps.add-comment.outputs.comment-id }}
+ comment_repo: ${{ steps.add-comment.outputs.comment-repo }}
+ comment_url: ${{ steps.add-comment.outputs.comment-url }}
model: ${{ steps.generate_aw_info.outputs.model }}
secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
slash_command: ${{ needs.pre_activation.outputs.matched_command }}
@@ -76,7 +77,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -91,7 +92,7 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
@@ -101,11 +102,11 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -126,9 +127,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -136,18 +137,31 @@ jobs:
GH_AW_WORKFLOW_FILE: "grumpy-reviewer.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_WORKFLOW_NAME: "Grumpy Code Reviewer 🔥"
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 😤 *Reluctantly reviewed by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"😤 *sigh* [{workflow_name}]({run_url}) is begrudgingly looking at this {event_type}... This better be worth my time.\",\"runSuccess\":\"😤 Fine. [{workflow_name}]({run_url}) finished the review. It wasn't completely terrible. I guess. 🙄\",\"runFailure\":\"😤 Great. [{workflow_name}]({run_url}) {status}. As if my day couldn't get any worse...\"}"
+ with:
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_workflow_run_comment.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -164,16 +178,16 @@ jobs:
GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }}
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request_review_comment, submit_pull_request_review, missing_tool, missing_data, noop
@@ -207,8 +221,9 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
- cat "/opt/gh-aw/prompts/pr_context_prompt.md"
+ cat "${GH_AW_HOME}/prompts/pr_context_prompt.md"
fi
cat << 'GH_AW_PROMPT_EOF'
@@ -226,9 +241,9 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -251,10 +266,10 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -280,11 +295,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -306,10 +321,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: grumpyreviewer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -331,16 +347,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -370,221 +390,49 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request_review_comment":{"max":5},"missing_data":{},"missing_tool":{},"noop":{"max":1},"submit_pull_request_review":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a review comment on a specific line of code in a pull request. Use this for inline code review feedback, suggestions, or questions about specific code changes. For general PR comments not tied to specific lines, use add_comment instead. When the workflow is configured with `target: \"*\"`, you must specify `pull_request_number` to indicate which PR to target. CONSTRAINTS: Maximum 5 review comment(s) can be created. Comments will be on the RIGHT side of the diff.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Review comment content in Markdown. Provide specific, actionable feedback about the code at this location.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "line": {
- "description": "Line number for the comment. For single-line comments, this is the target line. For multi-line comments, this is the ending line.",
- "type": [
- "number",
- "string"
- ]
- },
- "path": {
- "description": "File path relative to the repository root (e.g., 'src/auth/login.js'). Must be a file that was changed in the PR.",
- "type": "string"
- },
- "pull_request_number": {
- "description": "Pull request number to add the review comment to. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, adds the comment to the PR that triggered this workflow. Required when the workflow target is '*' (any PR) — omitting it will cause the comment to fail.",
- "type": [
- "number",
- "string"
- ]
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. If omitted, uses the configured target repository. Must be in the allowed-repos list if specified.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "side": {
- "description": "Side of the diff to comment on: RIGHT for the new version (additions), LEFT for the old version (deletions). Defaults to RIGHT.",
- "enum": [
- "LEFT",
- "RIGHT"
- ],
- "type": "string"
- },
- "start_line": {
- "description": "Starting line number for multi-line comments. When set, the comment spans from start_line to line. Omit for single-line comments.",
- "type": [
- "number",
- "string"
- ]
- }
- },
- "required": [
- "path",
- "line",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request_review_comment"
- },
- {
- "description": "Submit a pull request review with a status decision. All create_pull_request_review_comment outputs are automatically collected and included as inline comments in this review. Use APPROVE to approve the PR, REQUEST_CHANGES to request changes, or COMMENT for general feedback without a decision. If you don't call this tool, review comments are still submitted as a COMMENT review. CONSTRAINTS: Maximum 1 review(s) can be submitted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Overall review summary in Markdown. Provide a high-level assessment of the changes. Required for REQUEST_CHANGES; optional for APPROVE and COMMENT.",
- "type": "string"
- },
- "event": {
- "description": "Review decision: APPROVE to approve the pull request, REQUEST_CHANGES to formally request changes before merging, or COMMENT for general feedback without a formal decision. Defaults to COMMENT when omitted.",
- "enum": [
- "APPROVE",
- "REQUEST_CHANGES",
- "COMMENT"
- ],
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "submit_pull_request_review"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request_review_comment": " CONSTRAINTS: Maximum 5 review comment(s) can be created. Comments will be on the RIGHT side of the diff.",
+ "submit_pull_request_review": " CONSTRAINTS: Maximum 1 review(s) can be submitted."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request_review_comment": {
"defaultMax": 1,
@@ -700,6 +548,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -724,8 +573,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -736,7 +585,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -744,6 +593,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -761,10 +612,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -776,6 +627,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "pull_requests,repos"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -783,6 +640,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -800,7 +664,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -809,7 +674,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -837,7 +702,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -875,15 +740,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -893,7 +758,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -911,9 +776,9 @@ jobs:
GH_AW_COMMAND: grumpy
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -922,18 +787,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1014,9 +879,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1039,7 +904,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1066,9 +931,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1112,6 +977,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-grumpy-reviewer"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1127,7 +994,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1151,9 +1018,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1164,9 +1031,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1187,9 +1054,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1204,9 +1071,28 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
+ await main();
+ - name: Update reaction comment with completion status
+ id: conclusion
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ env.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: "Grumpy Code Reviewer 🔥"
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.agent.outputs.detection_conclusion }}
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 😤 *Reluctantly reviewed by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"😤 *sigh* [{workflow_name}]({run_url}) is begrudgingly looking at this {event_type}... This better be worth my time.\",\"runSuccess\":\"😤 Fine. [{workflow_name}]({run_url}) finished the review. It wasn't completely terrible. I guess. 🙄\",\"runFailure\":\"😤 Great. [{workflow_name}]({run_url}) {status}. As if my day couldn't get any worse...\"}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/notify_comment_error.cjs');
await main();
pre_activation:
@@ -1231,7 +1117,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for command workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1240,9 +1126,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check command position
id: check_command_position
@@ -1251,9 +1137,9 @@ jobs:
GH_AW_COMMANDS: "[\"grumpy\"]"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_command_position.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_command_position.cjs');
await main();
safe_outputs:
@@ -1267,6 +1153,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/grumpy-reviewer"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 😤 *Reluctantly reviewed by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"😤 *sigh* [{workflow_name}]({run_url}) is begrudgingly looking at this {event_type}... This better be worth my time.\",\"runSuccess\":\"😤 Fine. [{workflow_name}]({run_url}) finished the review. It wasn't completely terrible. I guess. 🙄\",\"runFailure\":\"😤 Great. [{workflow_name}]({run_url}) {status}. As if my day couldn't get any worse...\"}"
GH_AW_WORKFLOW_ID: "grumpy-reviewer"
GH_AW_WORKFLOW_NAME: "Grumpy Code Reviewer 🔥"
@@ -1288,7 +1175,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1314,9 +1201,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1333,6 +1220,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: grumpyreviewer
steps:
- name: Checkout actions folder
@@ -1345,7 +1233,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/hourly-ci-cleaner.lock.yml b/.github/workflows/hourly-ci-cleaner.lock.yml
index 686d29a7917..173234343e2 100644
--- a/.github/workflows/hourly-ci-cleaner.lock.yml
+++ b/.github/workflows/hourly-ci-cleaner.lock.yml
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,18 +80,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","go"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -109,9 +109,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "hourly-ci-cleaner.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -129,20 +129,20 @@ jobs:
GH_AW_NEEDS_CHECK_CI_STATUS_OUTPUTS_CI_RUN_ID: ${{ needs.check_ci_status.outputs.ci_run_id }}
GH_AW_NEEDS_CHECK_CI_STATUS_OUTPUTS_CI_STATUS: ${{ needs.check_ci_status.outputs.ci_status }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -174,6 +174,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -194,9 +195,9 @@ jobs:
GH_AW_NEEDS_CHECK_CI_STATUS_OUTPUTS_CI_STATUS: ${{ needs.check_ci_status.outputs.ci_status }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -215,10 +216,10 @@ jobs:
GH_AW_NEEDS_CHECK_CI_STATUS_OUTPUTS_CI_STATUS: ${{ needs.check_ci_status.outputs.ci_status }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -240,11 +241,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -273,10 +274,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: hourlycicleaner
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -298,7 +300,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -310,9 +312,9 @@ jobs:
GH_AW_AGENT_IMPORT_SPEC: "../agents/ci-cleaner.agent.md"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/merge_remote_agent_github_folder.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/merge_remote_agent_github_folder.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -322,7 +324,11 @@ jobs:
cache-dependency-path: 'actions/setup/js/package-lock.json'
package-manager-cache: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Install Make
run: |
sudo apt-get update
@@ -360,16 +366,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -378,167 +384,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_missing_tool_issue":{"max":1,"title_prefix":"[missing tool]"},"create_pull_request":{"expires":48,"max":1,"title_prefix":"[ca] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[ca] \".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[ca] \"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -635,6 +504,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -659,8 +529,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -671,7 +541,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -679,7 +549,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -697,10 +568,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -708,10 +579,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -719,6 +595,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -736,7 +619,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -745,7 +629,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --mount /opt/hostedtoolcache/go:/opt/hostedtoolcache/go:ro --mount /usr/bin/go:/usr/bin/go:ro --mount /usr/bin/make:/usr/bin/make:ro --mount /usr/local/bin/node:/usr/local/bin/node:ro --mount /usr/local/bin/npm:/usr/local/bin/npm:ro --mount /usr/local/lib/node_modules:/usr/local/lib/node_modules:ro --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --mount /opt/hostedtoolcache/go:/opt/hostedtoolcache/go:ro --mount /usr/bin/go:/usr/bin/go:ro --mount /usr/bin/make:/usr/bin/make:ro --mount /usr/local/bin/node:/usr/local/bin/node:ro --mount /usr/local/bin/npm:/usr/local/bin/npm:ro --mount /usr/local/lib/node_modules:/usr/local/lib/node_modules:ro --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --agent ci-cleaner --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -773,7 +657,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -811,15 +695,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -829,7 +713,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -846,9 +730,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -857,18 +741,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -944,9 +828,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -969,7 +853,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -996,9 +880,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1089,6 +973,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-hourly-ci-cleaner"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1104,7 +990,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1129,9 +1015,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1145,9 +1031,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1170,9 +1056,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1188,9 +1074,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1203,9 +1089,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
safe_outputs:
@@ -1222,6 +1108,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/hourly-ci-cleaner"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "hourly-ci-cleaner"
GH_AW_WORKFLOW_ID: "hourly-ci-cleaner"
GH_AW_WORKFLOW_NAME: "CI Cleaner"
@@ -1245,7 +1132,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1300,9 +1187,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/instructions-janitor.lock.yml b/.github/workflows/instructions-janitor.lock.yml
index 29cbcbb3e25..bee0f770a80 100644
--- a/.github/workflows/instructions-janitor.lock.yml
+++ b/.github/workflows/instructions-janitor.lock.yml
@@ -60,7 +60,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -75,18 +75,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -104,9 +104,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "instructions-janitor.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -121,21 +121,21 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -167,6 +167,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -180,9 +181,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -201,10 +202,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -226,11 +227,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -255,10 +256,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: instructionsjanitor
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -279,16 +281,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -318,9 +324,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -328,7 +334,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -339,167 +345,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request":{"expires":48,"max":1,"title_prefix":"[instructions] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[instructions] \". Labels [\"documentation\" \"automation\" \"instructions\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[instructions] \". Labels [\"documentation\" \"automation\" \"instructions\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -596,6 +465,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -620,8 +490,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -632,7 +502,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -640,7 +510,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -658,19 +529,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -678,6 +554,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -695,7 +578,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -798,7 +682,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(cat .github/aw/github-agentic-workflows.md),Bash(cat),Bash(date),Bash(echo),Bash(git add:*),Bash(git branch:*),Bash(git checkout:*),Bash(git commit:*),Bash(git describe --tags --abbrev=0),Bash(git log --since='\''\'\'''\''*'\''\'\'''\'' --pretty=format:'\''\'\'''\''%h %s'\''\'\'''\'' -- docs/),Bash(git merge:*),Bash(git rm:*),Bash(git status),Bash(git switch:*),Bash(grep),Bash(head),Bash(ls),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc -l .github/aw/github-agentic-workflows.md),Bash(wc),Bash(yq),BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -842,15 +726,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -860,7 +744,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -872,14 +756,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -888,18 +772,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -979,9 +863,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1014,7 +898,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1042,9 +926,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1089,6 +973,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-instructions-janitor"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1104,7 +990,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1128,9 +1014,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1141,9 +1027,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1164,9 +1050,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1181,9 +1067,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1195,9 +1081,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
safe_outputs:
@@ -1214,6 +1100,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/instructions-janitor"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "instructions-janitor"
GH_AW_WORKFLOW_NAME: "Instructions Janitor"
outputs:
@@ -1236,7 +1123,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1283,7 +1170,7 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"allowed_files\":[\".github/aw/**\"],\"draft\":false,\"expires\":48,\"labels\":[\"documentation\",\"automation\",\"instructions\"],\"max\":1,\"max_patch_size\":1024,\"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\",\"CLAUDE.md\"],\"protected_files_policy\":\"allowed\",\"protected_path_prefixes\":[\".github/\",\".agents/\",\".claude/\"],\"title_prefix\":\"[instructions] \"},\"missing_data\":{},\"missing_tool\":{}}"
@@ -1291,9 +1178,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1310,6 +1197,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: instructionsjanitor
steps:
- name: Checkout actions folder
@@ -1322,7 +1210,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/issue-arborist.lock.yml b/.github/workflows/issue-arborist.lock.yml
index e72e4cbc79f..3906a774fa3 100644
--- a/.github/workflows/issue-arborist.lock.yml
+++ b/.github/workflows/issue-arborist.lock.yml
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,7 +80,7 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
@@ -90,11 +90,11 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate CODEX_API_KEY or OPENAI_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
@@ -113,9 +113,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "issue-arborist.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -130,15 +130,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, create_discussion, link_sub_issue, missing_tool, missing_data, noop
@@ -172,6 +172,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -192,9 +193,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -210,10 +211,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -232,11 +233,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -260,10 +261,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: issuearborist
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -284,13 +286,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
- env:
@@ -321,9 +327,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -333,236 +339,41 @@ jobs:
- name: Install Codex
run: npm install -g @openai/codex@latest
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"create_issue":{"expires":48,"group":true,"max":5},"link_sub_issue":{"max":50},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 5 issue(s) can be created. Title will be prefixed with \"[Parent] \".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[Issue Arborist] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Link an issue as a sub-issue of a parent issue. Use this to establish parent-child relationships between issues for better organization and tracking of related work items. CONSTRAINTS: Maximum 50 sub-issue link(s) can be created.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "parent_issue_number": {
- "description": "The parent issue number to link the sub-issue to. This is the numeric ID from the GitHub URL (e.g., 100 in github.com/owner/repo/issues/100).",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "sub_issue_number": {
- "description": "The issue number to link as a sub-issue of the parent. This is the numeric ID from the GitHub URL (e.g., 101 in github.com/owner/repo/issues/101).",
- "type": [
- "number",
- "string"
- ]
- }
- },
- "required": [
- "parent_issue_number",
- "sub_issue_number"
- ],
- "type": "object"
- },
- "name": "link_sub_issue"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[Issue Arborist] \". Discussions will be created in category \"audits\".",
+ "create_issue": " CONSTRAINTS: Maximum 5 issue(s) can be created. Title will be prefixed with \"[Parent] \".",
+ "link_sub_issue": " CONSTRAINTS: Maximum 50 sub-issue link(s) can be created."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -700,6 +511,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -724,8 +536,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -736,7 +548,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -744,6 +556,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -761,7 +575,7 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="codex"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
cat > /tmp/gh-aw/mcp-config/config.toml << GH_AW_MCP_CONFIG_EOF
[history]
@@ -785,10 +599,15 @@ jobs:
[mcp_servers.safeoutputs.headers]
Authorization = "$GH_AW_SAFE_OUTPUTS_API_KEY"
+
+ [mcp_servers.safeoutputs."guard-policies"]
+
+ [mcp_servers.safeoutputs."guard-policies".write-sink]
+ accept = ["*"]
GH_AW_MCP_CONFIG_EOF
# Generate JSON config for MCP gateway
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -799,6 +618,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "issues"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -806,6 +631,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -823,13 +655,14 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Codex
run: |
set -o pipefail
mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,172.30.0.1,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,172.30.0.1,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" && codex ${GH_AW_MODEL_AGENT_CODEX:+-c model="$GH_AW_MODEL_AGENT_CODEX" }exec -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
@@ -868,15 +701,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'CODEX_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY'
@@ -887,7 +720,7 @@ jobs:
SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -899,14 +732,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,172.30.0.1,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,172.30.0.1,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -915,18 +748,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_codex_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_codex_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1001,9 +834,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1016,7 +849,7 @@ jobs:
set -o pipefail
mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,host.docker.internal,openai.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "172.30.0.1,api.openai.com,host.docker.internal,openai.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" && codex ${GH_AW_MODEL_DETECTION_CODEX:+-c model="$GH_AW_MODEL_DETECTION_CODEX" }exec -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
@@ -1040,9 +873,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1086,6 +919,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-issue-arborist"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1101,7 +936,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1125,9 +960,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1138,9 +973,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1161,9 +996,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1178,9 +1013,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1195,6 +1030,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/issue-arborist"
GH_AW_ENGINE_ID: "codex"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "issue-arborist"
GH_AW_WORKFLOW_NAME: "Issue Arborist"
outputs:
@@ -1217,7 +1053,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1236,16 +1072,16 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,172.30.0.1,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,172.30.0.1,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[Issue Arborist] \"},\"create_issue\":{\"expires\":48,\"group\":true,\"max\":5,\"title_prefix\":\"[Parent] \"},\"link_sub_issue\":{\"max\":50},\"missing_data\":{},\"missing_tool\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml
index ab6650f3874..0d97e78d9e9 100644
--- a/.github/workflows/issue-monster.lock.yml
+++ b/.github/workflows/issue-monster.lock.yml
@@ -27,10 +27,13 @@
# Imports:
# - shared/activation-app.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"429c9553c1b8706ab97bd48fe931129d4fdfe815c78402277377062529800d8a","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"ece77a4b96614fc08a51dbbdec2d9058cd2270d7057ce1378e3b1eded2504679","strict":true}
name: "Issue Monster"
"on":
+ # permissions: # Permissions applied to pre-activation job
+ # issues: read
+ # pull-requests: read
schedule:
- cron: "*/30 * * * *"
# Friendly format: every 30m
@@ -38,29 +41,352 @@ name: "Issue Monster"
# max: 5
# query: is:pr is:open is:draft author:app/copilot-swe-agent
# skip-if-no-match: is:issue is:open # Skip-if-no-match processed as search check in pre-activation job
+ # steps: # Steps injected into pre-activation job
+ # - id: search
+ # name: Search for candidate issues
+ # uses: actions/github-script@v8
+ # with:
+ # script: |
+ # const { owner, repo } = context.repo;
+ #
+ # try {
+ # // Check for recent rate-limited PRs to avoid scheduling more work during rate limiting
+ # core.info('Checking for recent rate-limited PRs...');
+ # const rateLimitCheckDate = new Date();
+ # rateLimitCheckDate.setHours(rateLimitCheckDate.getHours() - 1); // Check last hour
+ # // Format as YYYY-MM-DDTHH:MM:SS for GitHub search API
+ # const rateLimitCheckISO = rateLimitCheckDate.toISOString().split('.')[0] + 'Z';
+ #
+ # const recentPRsQuery = `is:pr author:app/copilot-swe-agent created:>${rateLimitCheckISO} repo:${owner}/${repo}`;
+ # const recentPRsResponse = await github.rest.search.issuesAndPullRequests({
+ # q: recentPRsQuery,
+ # per_page: 10,
+ # sort: 'created',
+ # order: 'desc'
+ # });
+ #
+ # core.info(`Found ${recentPRsResponse.data.total_count} recent Copilot PRs to check for rate limiting`);
+ #
+ # // Check if any recent PRs have rate limit indicators
+ # let rateLimitDetected = false;
+ # for (const pr of recentPRsResponse.data.items) {
+ # try {
+ # const prTimelineQuery = `
+ # query($owner: String!, $repo: String!, $number: Int!) {
+ # repository(owner: $owner, name: $repo) {
+ # pullRequest(number: $number) {
+ # timelineItems(first: 50, itemTypes: [ISSUE_COMMENT]) {
+ # nodes {
+ # __typename
+ # ... on IssueComment {
+ # body
+ # createdAt
+ # }
+ # }
+ # }
+ # }
+ # }
+ # }
+ # `;
+ #
+ # const prTimelineResult = await github.graphql(prTimelineQuery, {
+ # owner,
+ # repo,
+ # number: pr.number
+ # });
+ #
+ # const comments = prTimelineResult?.repository?.pullRequest?.timelineItems?.nodes || [];
+ # const rateLimitPattern = /rate limit|API rate limit|secondary rate limit|abuse detection|429|too many requests/i;
+ #
+ # for (const comment of comments) {
+ # if (comment.body && rateLimitPattern.test(comment.body)) {
+ # core.warning(`Rate limiting detected in PR #${pr.number}: ${comment.body.substring(0, 200)}`);
+ # rateLimitDetected = true;
+ # break;
+ # }
+ # }
+ #
+ # if (rateLimitDetected) break;
+ # } catch (error) {
+ # core.warning(`Could not check PR #${pr.number} for rate limiting: ${error.message}`);
+ # }
+ # }
+ #
+ # if (rateLimitDetected) {
+ # core.warning('🛑 Rate limiting detected in recent PRs. Skipping issue assignment to prevent further rate limit issues.');
+ # core.setOutput('issue_count', 0);
+ # core.setOutput('issue_numbers', '');
+ # core.setOutput('issue_list', '');
+ # core.setOutput('has_issues', 'false');
+ # return;
+ # }
+ #
+ # core.info('✓ No rate limiting detected. Proceeding with issue search.');
+ #
+ # // Labels that indicate an issue should NOT be auto-assigned
+ # const excludeLabels = [
+ # 'wontfix',
+ # 'duplicate',
+ # 'invalid',
+ # 'question',
+ # 'discussion',
+ # 'needs-discussion',
+ # 'blocked',
+ # 'on-hold',
+ # 'waiting-for-feedback',
+ # 'needs-more-info',
+ # 'no-bot',
+ # 'no-campaign'
+ # ];
+ #
+ # // Labels that indicate an issue is a GOOD candidate for auto-assignment
+ # const priorityLabels = [
+ # 'good first issue',
+ # 'good-first-issue',
+ # 'bug',
+ # 'enhancement',
+ # 'feature',
+ # 'documentation',
+ # 'tech-debt',
+ # 'refactoring',
+ # 'performance',
+ # 'security'
+ # ];
+ #
+ # // Search for open issues with "cookie" label and without excluded labels
+ # // The "cookie" label indicates issues that are approved work queue items from automated workflows
+ # const query = `is:issue is:open repo:${owner}/${repo} label:cookie -label:"${excludeLabels.join('" -label:"')}"`;
+ # core.info(`Searching: ${query}`);
+ # const response = await github.rest.search.issuesAndPullRequests({
+ # q: query,
+ # per_page: 100,
+ # sort: 'created',
+ # order: 'desc'
+ # });
+ # core.info(`Found ${response.data.total_count} total issues matching basic criteria`);
+ #
+ # // Fetch full details for each issue to get labels, assignees, sub-issues, and linked PRs
+ # const issuesWithDetails = await Promise.all(
+ # response.data.items.map(async (issue) => {
+ # const fullIssue = await github.rest.issues.get({
+ # owner,
+ # repo,
+ # issue_number: issue.number
+ # });
+ #
+ # // Check if this issue has sub-issues and linked PRs using GraphQL
+ # let subIssuesCount = 0;
+ # let linkedPRs = [];
+ # try {
+ # const issueDetailsQuery = `
+ # query($owner: String!, $repo: String!, $number: Int!) {
+ # repository(owner: $owner, name: $repo) {
+ # issue(number: $number) {
+ # subIssues {
+ # totalCount
+ # }
+ # timelineItems(first: 100, itemTypes: [CROSS_REFERENCED_EVENT]) {
+ # nodes {
+ # ... on CrossReferencedEvent {
+ # source {
+ # __typename
+ # ... on PullRequest {
+ # number
+ # state
+ # isDraft
+ # author {
+ # login
+ # }
+ # }
+ # }
+ # }
+ # }
+ # }
+ # }
+ # }
+ # }
+ # `;
+ # const issueDetailsResult = await github.graphql(issueDetailsQuery, {
+ # owner,
+ # repo,
+ # number: issue.number
+ # });
+ #
+ # subIssuesCount = issueDetailsResult?.repository?.issue?.subIssues?.totalCount || 0;
+ #
+ # // Extract linked PRs from timeline
+ # const timelineItems = issueDetailsResult?.repository?.issue?.timelineItems?.nodes || [];
+ # linkedPRs = timelineItems
+ # .filter(item => item?.source?.__typename === 'PullRequest')
+ # .map(item => ({
+ # number: item.source.number,
+ # state: item.source.state,
+ # isDraft: item.source.isDraft,
+ # author: item.source.author?.login
+ # }));
+ #
+ # core.info(`Issue #${issue.number} has ${linkedPRs.length} linked PR(s)`);
+ # } catch (error) {
+ # // If GraphQL query fails, continue with defaults
+ # core.warning(`Could not check details for #${issue.number}: ${error.message}`);
+ # }
+ #
+ # return {
+ # ...fullIssue.data,
+ # subIssuesCount,
+ # linkedPRs
+ # };
+ # })
+ # );
+ #
+ # // Filter and score issues
+ # const scoredIssues = issuesWithDetails
+ # .filter(issue => {
+ # // Exclude issues that already have assignees
+ # if (issue.assignees && issue.assignees.length > 0) {
+ # core.info(`Skipping #${issue.number}: already has assignees`);
+ # return false;
+ # }
+ #
+ # // Exclude issues with excluded labels (double check)
+ # const issueLabels = issue.labels.map(l => l.name.toLowerCase());
+ # if (issueLabels.some(label => excludeLabels.map(l => l.toLowerCase()).includes(label))) {
+ # core.info(`Skipping #${issue.number}: has excluded label`);
+ # return false;
+ # }
+ #
+ # // Exclude issues with campaign labels (campaign:*)
+ # // Campaign items are managed by campaign orchestrators
+ # if (issueLabels.some(label => label.startsWith('campaign:'))) {
+ # core.info(`Skipping #${issue.number}: has campaign label (managed by campaign orchestrator)`);
+ # return false;
+ # }
+ #
+ # // Exclude issues that have sub-issues (parent/organizing issues)
+ # if (issue.subIssuesCount > 0) {
+ # core.info(`Skipping #${issue.number}: has ${issue.subIssuesCount} sub-issue(s) - parent issues are used for organizing, not tasks`);
+ # return false;
+ # }
+ #
+ # // Exclude issues with closed PRs (treat as complete)
+ # const closedPRs = issue.linkedPRs?.filter(pr => pr.state === 'CLOSED' || pr.state === 'MERGED') || [];
+ # if (closedPRs.length > 0) {
+ # core.info(`Skipping #${issue.number}: has ${closedPRs.length} closed/merged PR(s) - treating as complete`);
+ # return false;
+ # }
+ #
+ # // Exclude issues with open PRs from Copilot coding agent
+ # const openCopilotPRs = issue.linkedPRs?.filter(pr =>
+ # pr.state === 'OPEN' &&
+ # (pr.author === 'copilot-swe-agent' || pr.author?.includes('copilot'))
+ # ) || [];
+ # if (openCopilotPRs.length > 0) {
+ # core.info(`Skipping #${issue.number}: has ${openCopilotPRs.length} open PR(s) from Copilot - already being worked on`);
+ # return false;
+ # }
+ #
+ # return true;
+ # })
+ # .map(issue => {
+ # const issueLabels = issue.labels.map(l => l.name.toLowerCase());
+ # let score = 0;
+ #
+ # // Score based on priority labels (higher score = higher priority)
+ # if (issueLabels.includes('good first issue') || issueLabels.includes('good-first-issue')) {
+ # score += 50;
+ # }
+ # if (issueLabels.includes('bug')) {
+ # score += 40;
+ # }
+ # if (issueLabels.includes('security')) {
+ # score += 45;
+ # }
+ # if (issueLabels.includes('documentation')) {
+ # score += 35;
+ # }
+ # if (issueLabels.includes('enhancement') || issueLabels.includes('feature')) {
+ # score += 30;
+ # }
+ # if (issueLabels.includes('performance')) {
+ # score += 25;
+ # }
+ # if (issueLabels.includes('tech-debt') || issueLabels.includes('refactoring')) {
+ # score += 20;
+ # }
+ #
+ # // Bonus for issues with clear labels (any priority label)
+ # if (issueLabels.some(label => priorityLabels.map(l => l.toLowerCase()).includes(label))) {
+ # score += 10;
+ # }
+ #
+ # // Age bonus: older issues get slight priority (days old / 10)
+ # const ageInDays = Math.floor((Date.now() - new Date(issue.created_at)) / (1000 * 60 * 60 * 24));
+ # score += Math.min(ageInDays / 10, 20); // Cap age bonus at 20 points
+ #
+ # return {
+ # number: issue.number,
+ # title: issue.title,
+ # labels: issue.labels.map(l => l.name),
+ # created_at: issue.created_at,
+ # score
+ # };
+ # })
+ # .sort((a, b) => b.score - a.score); // Sort by score descending
+ #
+ # // Format output
+ # const issueList = scoredIssues.map(i => {
+ # const labelStr = i.labels.length > 0 ? ` [${i.labels.join(', ')}]` : '';
+ # return `#${i.number}: ${i.title}${labelStr} (score: ${i.score.toFixed(1)})`;
+ # }).join('\n');
+ #
+ # const issueNumbers = scoredIssues.map(i => i.number).join(',');
+ #
+ # core.info(`Total candidate issues after filtering: ${scoredIssues.length}`);
+ # if (scoredIssues.length > 0) {
+ # core.info(`Top candidates:\n${issueList.split('\n').slice(0, 10).join('\n')}`);
+ # }
+ #
+ # core.setOutput('issue_count', scoredIssues.length);
+ # core.setOutput('issue_numbers', issueNumbers);
+ # core.setOutput('issue_list', issueList);
+ #
+ # if (scoredIssues.length === 0) {
+ # core.info('🍽️ No suitable candidate issues - the plate is empty!');
+ # core.setOutput('has_issues', 'false');
+ # } else {
+ # core.setOutput('has_issues', 'true');
+ # }
+ # } catch (error) {
+ # core.error(`Error searching for issues: ${error.message}`);
+ # core.setOutput('issue_count', 0);
+ # core.setOutput('issue_numbers', '');
+ # core.setOutput('issue_list', '');
+ # core.setOutput('has_issues', 'false');
+ # }
workflow_dispatch:
permissions: {}
concurrency:
- group: "gh-aw-${{ github.workflow }}"
+ group: "gh-aw-${{ github.workflow }}-${{ github.event.issue.number || github.event.discussion.number || github.run_id }}"
run-name: "Issue Monster"
jobs:
activation:
- needs:
- - pre_activation
- - search_issues
- if: (needs.pre_activation.outputs.activated == 'true') && (needs.search_issues.outputs.has_issues == 'true')
+ needs: pre_activation
+ if: (needs.pre_activation.outputs.activated == 'true') && (needs.pre_activation.outputs.has_issues == 'true')
runs-on: ubuntu-slim
permissions:
contents: read
outputs:
+ body: ${{ steps.sanitized.outputs.body }}
comment_id: ""
comment_repo: ""
model: ${{ steps.generate_aw_info.outputs.model }}
secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
+ text: ${{ steps.sanitized.outputs.text }}
+ title: ${{ steps.sanitized.outputs.title }}
steps:
- name: Checkout actions folder
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -72,7 +398,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -87,7 +413,7 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
@@ -97,11 +423,11 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -119,9 +445,18 @@ jobs:
GH_AW_WORKFLOW_FILE: "issue-monster.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
+ await main();
+ - name: Compute current body text
+ id: sanitized
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ with:
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/compute_text.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -135,19 +470,19 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
- GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_COUNT: ${{ needs.search_issues.outputs.issue_count }}
- GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_LIST: ${{ needs.search_issues.outputs.issue_list }}
- GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_NUMBERS: ${{ needs.search_issues.outputs.issue_numbers }}
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_COUNT: ${{ needs.pre_activation.outputs.issue_count }}
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_LIST: ${{ needs.pre_activation.outputs.issue_list }}
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_NUMBERS: ${{ needs.pre_activation.outputs.issue_numbers }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, assign_to_agent, missing_tool, missing_data, noop
@@ -181,6 +516,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -196,14 +532,14 @@ jobs:
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
- GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_COUNT: ${{ needs.search_issues.outputs.issue_count }}
- GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_LIST: ${{ needs.search_issues.outputs.issue_list }}
- GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_NUMBERS: ${{ needs.search_issues.outputs.issue_numbers }}
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_COUNT: ${{ needs.pre_activation.outputs.issue_count }}
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_LIST: ${{ needs.pre_activation.outputs.issue_list }}
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_NUMBERS: ${{ needs.pre_activation.outputs.issue_numbers }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -218,15 +554,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
- GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_COUNT: ${{ needs.search_issues.outputs.issue_count }}
- GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_LIST: ${{ needs.search_issues.outputs.issue_list }}
- GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_NUMBERS: ${{ needs.search_issues.outputs.issue_numbers }}
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_COUNT: ${{ needs.pre_activation.outputs.issue_count }}
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_LIST: ${{ needs.pre_activation.outputs.issue_list }}
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_NUMBERS: ${{ needs.pre_activation.outputs.issue_numbers }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -241,19 +577,19 @@ jobs:
GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED,
- GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_COUNT: process.env.GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_COUNT,
- GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_LIST: process.env.GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_LIST,
- GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_NUMBERS: process.env.GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_NUMBERS
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_COUNT: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_COUNT,
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_LIST: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_LIST,
+ GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_NUMBERS: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_NUMBERS
}
});
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -265,25 +601,22 @@ jobs:
retention-days: 1
agent:
- needs:
- - activation
- - search_issues
+ needs: activation
runs-on: ubuntu-latest
permissions:
contents: read
issues: read
pull-requests: read
- concurrency:
- group: "gh-aw-copilot-${{ github.workflow }}"
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_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: issuemonster
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -305,13 +638,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -334,203 +671,49 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":3,"target":"*"},"assign_to_agent":{"allowed":["copilot"],"max":3,"target":"*"},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 3 comment(s) can be added. Target: *.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Assign the GitHub Copilot coding agent to work on an issue or pull request. The agent will analyze the issue/PR and attempt to implement a solution, creating a pull request when complete. Use this to delegate coding tasks to Copilot. Example usage: assign_to_agent(issue_number=123, agent=\"copilot\") or assign_to_agent(pull_number=456, agent=\"copilot\", pull_request_repo=\"owner/repo\") CONSTRAINTS: Maximum 3 issue(s) can be assigned to agent.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "agent": {
- "description": "Agent identifier to assign. Defaults to 'copilot' (the Copilot coding agent) if not specified.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "issue_number": {
- "description": "Issue number to assign the Copilot coding agent to. This is the numeric ID from the GitHub URL (e.g., 234 in github.com/owner/repo/issues/234). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from an issue created earlier in the same workflow run. The issue should contain clear, actionable requirements. Either issue_number or pull_number must be provided, but not both.",
- "type": [
- "number",
- "string"
- ]
- },
- "pull_number": {
- "description": "Pull request number to assign the Copilot coding agent to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/pull/456). Either issue_number or pull_number must be provided, but not both.",
- "type": [
- "number",
- "string"
- ]
- },
- "pull_request_repo": {
- "description": "Target repository where the pull request should be created, in 'owner/repo' format. If omitted, the PR will be created in the same repository as the issue. This allows issues and code to live in different repositories. The global pull-request-repo configuration (if set) is automatically allowed; additional repositories must be listed in allowed-pull-request-repos.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "assign_to_agent"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 3 comment(s) can be added. Target: *.",
+ "assign_to_agent": " CONSTRAINTS: Maximum 3 issue(s) can be assigned to agent."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -634,6 +817,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -658,8 +842,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -670,7 +854,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -678,6 +862,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -695,10 +881,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -710,6 +896,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -717,6 +909,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -734,7 +933,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -743,7 +943,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -771,7 +971,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -809,15 +1009,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -827,7 +1027,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -844,9 +1044,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -855,18 +1055,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -941,9 +1141,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -966,7 +1166,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -993,9 +1193,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1040,6 +1240,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-issue-monster"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1055,7 +1257,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1079,9 +1281,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1092,9 +1294,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1117,9 +1319,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1134,18 +1336,25 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
pre_activation:
runs-on: ubuntu-slim
permissions:
contents: read
+ issues: read
+ pull-requests: read
outputs:
activated: ${{ ((steps.check_membership.outputs.is_team_member == 'true') && (steps.check_skip_if_match.outputs.skip_check_ok == 'true')) && (steps.check_skip_if_no_match.outputs.skip_no_match_check_ok == 'true') }}
+ has_issues: ${{ steps.search.outputs.has_issues }}
+ issue_count: ${{ steps.search.outputs.issue_count }}
+ issue_list: ${{ steps.search.outputs.issue_list }}
+ issue_numbers: ${{ steps.search.outputs.issue_numbers }}
matched_command: ''
+ search_result: ${{ steps.search.outcome }}
steps:
- name: Checkout actions folder
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -1157,7 +1366,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1166,10 +1375,19 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
+ - name: Generate GitHub App token for skip-if checks
+ id: pre-activation-app-token
+ uses: actions/create-github-app-token@a7f885bf4560200d03183ed941cb6fb072e4b343 # v3.0.0-beta.4
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ owner: ${{ github.repository_owner }}
+ repositories: ${{ github.event.repository.name }}
+ github-api-url: ${{ github.api_url }}
- name: Check skip-if-match query
id: check_skip_if_match
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1178,10 +1396,11 @@ jobs:
GH_AW_WORKFLOW_NAME: "Issue Monster"
GH_AW_SKIP_MAX_MATCHES: "5"
with:
+ github-token: ${{ steps.pre-activation-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_skip_if_match.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_skip_if_match.cjs');
await main();
- name: Check skip-if-no-match query
id: check_skip_if_no_match
@@ -1191,122 +1410,15 @@ jobs:
GH_AW_WORKFLOW_NAME: "Issue Monster"
GH_AW_SKIP_MIN_MATCHES: "1"
with:
+ github-token: ${{ steps.pre-activation-app-token.outputs.token }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_skip_if_no_match.cjs');
- await main();
-
- safe_outputs:
- needs: agent
- if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true')
- runs-on: ubuntu-slim
- permissions:
- contents: read
- discussions: write
- issues: write
- pull-requests: write
- timeout-minutes: 15
- env:
- GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/issue-monster"
- GH_AW_ENGINE_ID: "copilot"
- GH_AW_ENGINE_MODEL: "gpt-5.1-codex-mini"
- GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🍪 *Om nom nom by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🍪 ISSUE! ISSUE! [{workflow_name}]({run_url}) hungry for issues on this {event_type}! Om nom nom...\",\"runSuccess\":\"🍪 YUMMY! [{workflow_name}]({run_url}) ate the issues! That was DELICIOUS! Me want MORE! 😋\",\"runFailure\":\"🍪 Aww... [{workflow_name}]({run_url}) {status}. No cookie for monster today... 😢\"}"
- GH_AW_WORKFLOW_ID: "issue-monster"
- GH_AW_WORKFLOW_NAME: "Issue Monster"
- outputs:
- assign_to_agent_assigned: ${{ steps.assign_to_agent.outputs.assigned }}
- assign_to_agent_assignment_error_count: ${{ steps.assign_to_agent.outputs.assignment_error_count }}
- assign_to_agent_assignment_errors: ${{ steps.assign_to_agent.outputs.assignment_errors }}
- code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
- code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
- comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }}
- comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }}
- create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
- create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
- process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
- process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
- steps:
- - name: Checkout actions folder
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- with:
- repository: github/gh-aw
- sparse-checkout: |
- actions
- persist-credentials: false
- - name: Setup Scripts
- uses: ./actions/setup
- with:
- destination: /opt/gh-aw/actions
- - name: Download agent output artifact
- id: download-agent-output
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: agent
- path: /tmp/gh-aw/
- - name: Setup agent output environment variable
- if: steps.download-agent-output.outcome == 'success'
- run: |
- mkdir -p /tmp/gh-aw/
- find "/tmp/gh-aw/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV"
- - name: Process Safe Outputs
- id: process_safe_outputs
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":3,\"target\":\"*\"},\"missing_data\":{},\"missing_tool\":{}}"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
- await main();
- - name: Assign to agent
- id: assign_to_agent
- if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'assign_to_agent'))
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_AGENT_MAX_COUNT: 3
- GH_AW_AGENT_TARGET: "*"
- GH_AW_AGENT_ALLOWED: "copilot"
- with:
- github-token: ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/assign_to_agent.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_skip_if_no_match.cjs');
await main();
- - name: Upload Safe Output Items Manifest
- if: always()
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
- with:
- name: safe-output-items
- path: /tmp/safe-output-items.jsonl
- if-no-files-found: warn
-
- search_issues:
- needs: pre_activation
- if: needs.pre_activation.outputs.activated == 'true'
- runs-on: ubuntu-latest
- permissions:
- issues: read
-
- outputs:
- has_issues: ${{ steps.search.outputs.has_issues }}
- issue_count: ${{ steps.search.outputs.issue_count }}
- issue_list: ${{ steps.search.outputs.issue_list }}
- issue_numbers: ${{ steps.search.outputs.issue_numbers }}
- steps:
- name: Search for candidate issues
id: search
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ uses: actions/github-script@v8
with:
script: |
const { owner, repo } = context.repo;
@@ -1626,3 +1738,98 @@ jobs:
core.setOutput('has_issues', 'false');
}
+ safe_outputs:
+ needs: agent
+ if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true')
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ discussions: write
+ issues: write
+ pull-requests: write
+ timeout-minutes: 15
+ env:
+ GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/issue-monster"
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_ENGINE_MODEL: "gpt-5.1-codex-mini"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🍪 *Om nom nom by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🍪 ISSUE! ISSUE! [{workflow_name}]({run_url}) hungry for issues on this {event_type}! Om nom nom...\",\"runSuccess\":\"🍪 YUMMY! [{workflow_name}]({run_url}) ate the issues! That was DELICIOUS! Me want MORE! 😋\",\"runFailure\":\"🍪 Aww... [{workflow_name}]({run_url}) {status}. No cookie for monster today... 😢\"}"
+ GH_AW_WORKFLOW_ID: "issue-monster"
+ GH_AW_WORKFLOW_NAME: "Issue Monster"
+ outputs:
+ assign_to_agent_assigned: ${{ steps.assign_to_agent.outputs.assigned }}
+ assign_to_agent_assignment_error_count: ${{ steps.assign_to_agent.outputs.assignment_error_count }}
+ assign_to_agent_assignment_errors: ${{ steps.assign_to_agent.outputs.assignment_errors }}
+ code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
+ code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
+ comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }}
+ comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }}
+ create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
+ create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
+ process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
+ process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
+ steps:
+ - name: Checkout actions folder
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ repository: github/gh-aw
+ sparse-checkout: |
+ actions
+ persist-credentials: false
+ - name: Setup Scripts
+ uses: ./actions/setup
+ with:
+ destination: ${{ env.GH_AW_HOME }}/actions
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV"
+ - name: Process Safe Outputs
+ id: process_safe_outputs
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
+ GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":3,\"target\":\"*\"},\"missing_data\":{},\"missing_tool\":{}}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
+ await main();
+ - name: Assign to agent
+ id: assign_to_agent
+ if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'assign_to_agent'))
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
+ GH_AW_AGENT_MAX_COUNT: 3
+ GH_AW_AGENT_TARGET: "*"
+ GH_AW_AGENT_ALLOWED: "copilot"
+ with:
+ github-token: ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/assign_to_agent.cjs');
+ await main();
+ - name: Upload Safe Output Items Manifest
+ if: always()
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
+ with:
+ name: safe-output-items
+ path: /tmp/safe-output-items.jsonl
+ if-no-files-found: warn
+
diff --git a/.github/workflows/issue-monster.md b/.github/workflows/issue-monster.md
index da0c5212fcb..cbeaf8b8c94 100644
--- a/.github/workflows/issue-monster.md
+++ b/.github/workflows/issue-monster.md
@@ -8,6 +8,332 @@ on:
query: "is:pr is:open is:draft author:app/copilot-swe-agent"
max: 5
skip-if-no-match: "is:issue is:open"
+ permissions:
+ issues: read
+ pull-requests: read
+ steps:
+ - name: Search for candidate issues
+ id: search
+ uses: actions/github-script@v8
+ with:
+ script: |
+ const { owner, repo } = context.repo;
+
+ try {
+ // Check for recent rate-limited PRs to avoid scheduling more work during rate limiting
+ core.info('Checking for recent rate-limited PRs...');
+ const rateLimitCheckDate = new Date();
+ rateLimitCheckDate.setHours(rateLimitCheckDate.getHours() - 1); // Check last hour
+ // Format as YYYY-MM-DDTHH:MM:SS for GitHub search API
+ const rateLimitCheckISO = rateLimitCheckDate.toISOString().split('.')[0] + 'Z';
+
+ const recentPRsQuery = `is:pr author:app/copilot-swe-agent created:>${rateLimitCheckISO} repo:${owner}/${repo}`;
+ const recentPRsResponse = await github.rest.search.issuesAndPullRequests({
+ q: recentPRsQuery,
+ per_page: 10,
+ sort: 'created',
+ order: 'desc'
+ });
+
+ core.info(`Found ${recentPRsResponse.data.total_count} recent Copilot PRs to check for rate limiting`);
+
+ // Check if any recent PRs have rate limit indicators
+ let rateLimitDetected = false;
+ for (const pr of recentPRsResponse.data.items) {
+ try {
+ const prTimelineQuery = `
+ query($owner: String!, $repo: String!, $number: Int!) {
+ repository(owner: $owner, name: $repo) {
+ pullRequest(number: $number) {
+ timelineItems(first: 50, itemTypes: [ISSUE_COMMENT]) {
+ nodes {
+ __typename
+ ... on IssueComment {
+ body
+ createdAt
+ }
+ }
+ }
+ }
+ }
+ }
+ `;
+
+ const prTimelineResult = await github.graphql(prTimelineQuery, {
+ owner,
+ repo,
+ number: pr.number
+ });
+
+ const comments = prTimelineResult?.repository?.pullRequest?.timelineItems?.nodes || [];
+ const rateLimitPattern = /rate limit|API rate limit|secondary rate limit|abuse detection|429|too many requests/i;
+
+ for (const comment of comments) {
+ if (comment.body && rateLimitPattern.test(comment.body)) {
+ core.warning(`Rate limiting detected in PR #${pr.number}: ${comment.body.substring(0, 200)}`);
+ rateLimitDetected = true;
+ break;
+ }
+ }
+
+ if (rateLimitDetected) break;
+ } catch (error) {
+ core.warning(`Could not check PR #${pr.number} for rate limiting: ${error.message}`);
+ }
+ }
+
+ if (rateLimitDetected) {
+ core.warning('🛑 Rate limiting detected in recent PRs. Skipping issue assignment to prevent further rate limit issues.');
+ core.setOutput('issue_count', 0);
+ core.setOutput('issue_numbers', '');
+ core.setOutput('issue_list', '');
+ core.setOutput('has_issues', 'false');
+ return;
+ }
+
+ core.info('✓ No rate limiting detected. Proceeding with issue search.');
+
+ // Labels that indicate an issue should NOT be auto-assigned
+ const excludeLabels = [
+ 'wontfix',
+ 'duplicate',
+ 'invalid',
+ 'question',
+ 'discussion',
+ 'needs-discussion',
+ 'blocked',
+ 'on-hold',
+ 'waiting-for-feedback',
+ 'needs-more-info',
+ 'no-bot',
+ 'no-campaign'
+ ];
+
+ // Labels that indicate an issue is a GOOD candidate for auto-assignment
+ const priorityLabels = [
+ 'good first issue',
+ 'good-first-issue',
+ 'bug',
+ 'enhancement',
+ 'feature',
+ 'documentation',
+ 'tech-debt',
+ 'refactoring',
+ 'performance',
+ 'security'
+ ];
+
+ // Search for open issues with "cookie" label and without excluded labels
+ // The "cookie" label indicates issues that are approved work queue items from automated workflows
+ const query = `is:issue is:open repo:${owner}/${repo} label:cookie -label:"${excludeLabels.join('" -label:"')}"`;
+ core.info(`Searching: ${query}`);
+ const response = await github.rest.search.issuesAndPullRequests({
+ q: query,
+ per_page: 100,
+ sort: 'created',
+ order: 'desc'
+ });
+ core.info(`Found ${response.data.total_count} total issues matching basic criteria`);
+
+ // Fetch full details for each issue to get labels, assignees, sub-issues, and linked PRs
+ const issuesWithDetails = await Promise.all(
+ response.data.items.map(async (issue) => {
+ const fullIssue = await github.rest.issues.get({
+ owner,
+ repo,
+ issue_number: issue.number
+ });
+
+ // Check if this issue has sub-issues and linked PRs using GraphQL
+ let subIssuesCount = 0;
+ let linkedPRs = [];
+ try {
+ const issueDetailsQuery = `
+ query($owner: String!, $repo: String!, $number: Int!) {
+ repository(owner: $owner, name: $repo) {
+ issue(number: $number) {
+ subIssues {
+ totalCount
+ }
+ timelineItems(first: 100, itemTypes: [CROSS_REFERENCED_EVENT]) {
+ nodes {
+ ... on CrossReferencedEvent {
+ source {
+ __typename
+ ... on PullRequest {
+ number
+ state
+ isDraft
+ author {
+ login
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ `;
+ const issueDetailsResult = await github.graphql(issueDetailsQuery, {
+ owner,
+ repo,
+ number: issue.number
+ });
+
+ subIssuesCount = issueDetailsResult?.repository?.issue?.subIssues?.totalCount || 0;
+
+ // Extract linked PRs from timeline
+ const timelineItems = issueDetailsResult?.repository?.issue?.timelineItems?.nodes || [];
+ linkedPRs = timelineItems
+ .filter(item => item?.source?.__typename === 'PullRequest')
+ .map(item => ({
+ number: item.source.number,
+ state: item.source.state,
+ isDraft: item.source.isDraft,
+ author: item.source.author?.login
+ }));
+
+ core.info(`Issue #${issue.number} has ${linkedPRs.length} linked PR(s)`);
+ } catch (error) {
+ // If GraphQL query fails, continue with defaults
+ core.warning(`Could not check details for #${issue.number}: ${error.message}`);
+ }
+
+ return {
+ ...fullIssue.data,
+ subIssuesCount,
+ linkedPRs
+ };
+ })
+ );
+
+ // Filter and score issues
+ const scoredIssues = issuesWithDetails
+ .filter(issue => {
+ // Exclude issues that already have assignees
+ if (issue.assignees && issue.assignees.length > 0) {
+ core.info(`Skipping #${issue.number}: already has assignees`);
+ return false;
+ }
+
+ // Exclude issues with excluded labels (double check)
+ const issueLabels = issue.labels.map(l => l.name.toLowerCase());
+ if (issueLabels.some(label => excludeLabels.map(l => l.toLowerCase()).includes(label))) {
+ core.info(`Skipping #${issue.number}: has excluded label`);
+ return false;
+ }
+
+ // Exclude issues with campaign labels (campaign:*)
+ // Campaign items are managed by campaign orchestrators
+ if (issueLabels.some(label => label.startsWith('campaign:'))) {
+ core.info(`Skipping #${issue.number}: has campaign label (managed by campaign orchestrator)`);
+ return false;
+ }
+
+ // Exclude issues that have sub-issues (parent/organizing issues)
+ if (issue.subIssuesCount > 0) {
+ core.info(`Skipping #${issue.number}: has ${issue.subIssuesCount} sub-issue(s) - parent issues are used for organizing, not tasks`);
+ return false;
+ }
+
+ // Exclude issues with closed PRs (treat as complete)
+ const closedPRs = issue.linkedPRs?.filter(pr => pr.state === 'CLOSED' || pr.state === 'MERGED') || [];
+ if (closedPRs.length > 0) {
+ core.info(`Skipping #${issue.number}: has ${closedPRs.length} closed/merged PR(s) - treating as complete`);
+ return false;
+ }
+
+ // Exclude issues with open PRs from Copilot coding agent
+ const openCopilotPRs = issue.linkedPRs?.filter(pr =>
+ pr.state === 'OPEN' &&
+ (pr.author === 'copilot-swe-agent' || pr.author?.includes('copilot'))
+ ) || [];
+ if (openCopilotPRs.length > 0) {
+ core.info(`Skipping #${issue.number}: has ${openCopilotPRs.length} open PR(s) from Copilot - already being worked on`);
+ return false;
+ }
+
+ return true;
+ })
+ .map(issue => {
+ const issueLabels = issue.labels.map(l => l.name.toLowerCase());
+ let score = 0;
+
+ // Score based on priority labels (higher score = higher priority)
+ if (issueLabels.includes('good first issue') || issueLabels.includes('good-first-issue')) {
+ score += 50;
+ }
+ if (issueLabels.includes('bug')) {
+ score += 40;
+ }
+ if (issueLabels.includes('security')) {
+ score += 45;
+ }
+ if (issueLabels.includes('documentation')) {
+ score += 35;
+ }
+ if (issueLabels.includes('enhancement') || issueLabels.includes('feature')) {
+ score += 30;
+ }
+ if (issueLabels.includes('performance')) {
+ score += 25;
+ }
+ if (issueLabels.includes('tech-debt') || issueLabels.includes('refactoring')) {
+ score += 20;
+ }
+
+ // Bonus for issues with clear labels (any priority label)
+ if (issueLabels.some(label => priorityLabels.map(l => l.toLowerCase()).includes(label))) {
+ score += 10;
+ }
+
+ // Age bonus: older issues get slight priority (days old / 10)
+ const ageInDays = Math.floor((Date.now() - new Date(issue.created_at)) / (1000 * 60 * 60 * 24));
+ score += Math.min(ageInDays / 10, 20); // Cap age bonus at 20 points
+
+ return {
+ number: issue.number,
+ title: issue.title,
+ labels: issue.labels.map(l => l.name),
+ created_at: issue.created_at,
+ score
+ };
+ })
+ .sort((a, b) => b.score - a.score); // Sort by score descending
+
+ // Format output
+ const issueList = scoredIssues.map(i => {
+ const labelStr = i.labels.length > 0 ? ` [${i.labels.join(', ')}]` : '';
+ return `#${i.number}: ${i.title}${labelStr} (score: ${i.score.toFixed(1)})`;
+ }).join('\n');
+
+ const issueNumbers = scoredIssues.map(i => i.number).join(',');
+
+ core.info(`Total candidate issues after filtering: ${scoredIssues.length}`);
+ if (scoredIssues.length > 0) {
+ core.info(`Top candidates:\n${issueList.split('\n').slice(0, 10).join('\n')}`);
+ }
+
+ core.setOutput('issue_count', scoredIssues.length);
+ core.setOutput('issue_numbers', issueNumbers);
+ core.setOutput('issue_list', issueList);
+
+ if (scoredIssues.length === 0) {
+ core.info('🍽️ No suitable candidate issues - the plate is empty!');
+ core.setOutput('has_issues', 'false');
+ } else {
+ core.setOutput('has_issues', 'true');
+ }
+ } catch (error) {
+ core.error(`Error searching for issues: ${error.message}`);
+ core.setOutput('issue_count', 0);
+ core.setOutput('issue_numbers', '');
+ core.setOutput('issue_list', '');
+ core.setOutput('has_issues', 'false');
+ }
+
permissions:
contents: read
@@ -28,342 +354,15 @@ tools:
lockdown: true
toolsets: [default, pull_requests]
-if: needs.search_issues.outputs.has_issues == 'true'
+if: needs.pre_activation.outputs.has_issues == 'true'
jobs:
- search_issues:
- needs: ["pre_activation"]
- if: needs.pre_activation.outputs.activated == 'true'
- runs-on: ubuntu-latest
- permissions:
- issues: read
+ pre-activation:
outputs:
issue_count: ${{ steps.search.outputs.issue_count }}
issue_numbers: ${{ steps.search.outputs.issue_numbers }}
issue_list: ${{ steps.search.outputs.issue_list }}
has_issues: ${{ steps.search.outputs.has_issues }}
- steps:
- - name: Search for candidate issues
- id: search
- uses: actions/github-script@v8
- with:
- script: |
- const { owner, repo } = context.repo;
-
- try {
- // Check for recent rate-limited PRs to avoid scheduling more work during rate limiting
- core.info('Checking for recent rate-limited PRs...');
- const rateLimitCheckDate = new Date();
- rateLimitCheckDate.setHours(rateLimitCheckDate.getHours() - 1); // Check last hour
- // Format as YYYY-MM-DDTHH:MM:SS for GitHub search API
- const rateLimitCheckISO = rateLimitCheckDate.toISOString().split('.')[0] + 'Z';
-
- const recentPRsQuery = `is:pr author:app/copilot-swe-agent created:>${rateLimitCheckISO} repo:${owner}/${repo}`;
- const recentPRsResponse = await github.rest.search.issuesAndPullRequests({
- q: recentPRsQuery,
- per_page: 10,
- sort: 'created',
- order: 'desc'
- });
-
- core.info(`Found ${recentPRsResponse.data.total_count} recent Copilot PRs to check for rate limiting`);
-
- // Check if any recent PRs have rate limit indicators
- let rateLimitDetected = false;
- for (const pr of recentPRsResponse.data.items) {
- try {
- const prTimelineQuery = `
- query($owner: String!, $repo: String!, $number: Int!) {
- repository(owner: $owner, name: $repo) {
- pullRequest(number: $number) {
- timelineItems(first: 50, itemTypes: [ISSUE_COMMENT]) {
- nodes {
- __typename
- ... on IssueComment {
- body
- createdAt
- }
- }
- }
- }
- }
- }
- `;
-
- const prTimelineResult = await github.graphql(prTimelineQuery, {
- owner,
- repo,
- number: pr.number
- });
-
- const comments = prTimelineResult?.repository?.pullRequest?.timelineItems?.nodes || [];
- const rateLimitPattern = /rate limit|API rate limit|secondary rate limit|abuse detection|429|too many requests/i;
-
- for (const comment of comments) {
- if (comment.body && rateLimitPattern.test(comment.body)) {
- core.warning(`Rate limiting detected in PR #${pr.number}: ${comment.body.substring(0, 200)}`);
- rateLimitDetected = true;
- break;
- }
- }
-
- if (rateLimitDetected) break;
- } catch (error) {
- core.warning(`Could not check PR #${pr.number} for rate limiting: ${error.message}`);
- }
- }
-
- if (rateLimitDetected) {
- core.warning('🛑 Rate limiting detected in recent PRs. Skipping issue assignment to prevent further rate limit issues.');
- core.setOutput('issue_count', 0);
- core.setOutput('issue_numbers', '');
- core.setOutput('issue_list', '');
- core.setOutput('has_issues', 'false');
- return;
- }
-
- core.info('✓ No rate limiting detected. Proceeding with issue search.');
-
- // Labels that indicate an issue should NOT be auto-assigned
- const excludeLabels = [
- 'wontfix',
- 'duplicate',
- 'invalid',
- 'question',
- 'discussion',
- 'needs-discussion',
- 'blocked',
- 'on-hold',
- 'waiting-for-feedback',
- 'needs-more-info',
- 'no-bot',
- 'no-campaign'
- ];
-
- // Labels that indicate an issue is a GOOD candidate for auto-assignment
- const priorityLabels = [
- 'good first issue',
- 'good-first-issue',
- 'bug',
- 'enhancement',
- 'feature',
- 'documentation',
- 'tech-debt',
- 'refactoring',
- 'performance',
- 'security'
- ];
-
- // Search for open issues with "cookie" label and without excluded labels
- // The "cookie" label indicates issues that are approved work queue items from automated workflows
- const query = `is:issue is:open repo:${owner}/${repo} label:cookie -label:"${excludeLabels.join('" -label:"')}"`;
- core.info(`Searching: ${query}`);
- const response = await github.rest.search.issuesAndPullRequests({
- q: query,
- per_page: 100,
- sort: 'created',
- order: 'desc'
- });
- core.info(`Found ${response.data.total_count} total issues matching basic criteria`);
-
- // Fetch full details for each issue to get labels, assignees, sub-issues, and linked PRs
- const issuesWithDetails = await Promise.all(
- response.data.items.map(async (issue) => {
- const fullIssue = await github.rest.issues.get({
- owner,
- repo,
- issue_number: issue.number
- });
-
- // Check if this issue has sub-issues and linked PRs using GraphQL
- let subIssuesCount = 0;
- let linkedPRs = [];
- try {
- const issueDetailsQuery = `
- query($owner: String!, $repo: String!, $number: Int!) {
- repository(owner: $owner, name: $repo) {
- issue(number: $number) {
- subIssues {
- totalCount
- }
- timelineItems(first: 100, itemTypes: [CROSS_REFERENCED_EVENT]) {
- nodes {
- ... on CrossReferencedEvent {
- source {
- __typename
- ... on PullRequest {
- number
- state
- isDraft
- author {
- login
- }
- }
- }
- }
- }
- }
- }
- }
- }
- `;
- const issueDetailsResult = await github.graphql(issueDetailsQuery, {
- owner,
- repo,
- number: issue.number
- });
-
- subIssuesCount = issueDetailsResult?.repository?.issue?.subIssues?.totalCount || 0;
-
- // Extract linked PRs from timeline
- const timelineItems = issueDetailsResult?.repository?.issue?.timelineItems?.nodes || [];
- linkedPRs = timelineItems
- .filter(item => item?.source?.__typename === 'PullRequest')
- .map(item => ({
- number: item.source.number,
- state: item.source.state,
- isDraft: item.source.isDraft,
- author: item.source.author?.login
- }));
-
- core.info(`Issue #${issue.number} has ${linkedPRs.length} linked PR(s)`);
- } catch (error) {
- // If GraphQL query fails, continue with defaults
- core.warning(`Could not check details for #${issue.number}: ${error.message}`);
- }
-
- return {
- ...fullIssue.data,
- subIssuesCount,
- linkedPRs
- };
- })
- );
-
- // Filter and score issues
- const scoredIssues = issuesWithDetails
- .filter(issue => {
- // Exclude issues that already have assignees
- if (issue.assignees && issue.assignees.length > 0) {
- core.info(`Skipping #${issue.number}: already has assignees`);
- return false;
- }
-
- // Exclude issues with excluded labels (double check)
- const issueLabels = issue.labels.map(l => l.name.toLowerCase());
- if (issueLabels.some(label => excludeLabels.map(l => l.toLowerCase()).includes(label))) {
- core.info(`Skipping #${issue.number}: has excluded label`);
- return false;
- }
-
- // Exclude issues with campaign labels (campaign:*)
- // Campaign items are managed by campaign orchestrators
- if (issueLabels.some(label => label.startsWith('campaign:'))) {
- core.info(`Skipping #${issue.number}: has campaign label (managed by campaign orchestrator)`);
- return false;
- }
-
- // Exclude issues that have sub-issues (parent/organizing issues)
- if (issue.subIssuesCount > 0) {
- core.info(`Skipping #${issue.number}: has ${issue.subIssuesCount} sub-issue(s) - parent issues are used for organizing, not tasks`);
- return false;
- }
-
- // Exclude issues with closed PRs (treat as complete)
- const closedPRs = issue.linkedPRs?.filter(pr => pr.state === 'CLOSED' || pr.state === 'MERGED') || [];
- if (closedPRs.length > 0) {
- core.info(`Skipping #${issue.number}: has ${closedPRs.length} closed/merged PR(s) - treating as complete`);
- return false;
- }
-
- // Exclude issues with open PRs from Copilot coding agent
- const openCopilotPRs = issue.linkedPRs?.filter(pr =>
- pr.state === 'OPEN' &&
- (pr.author === 'copilot-swe-agent' || pr.author?.includes('copilot'))
- ) || [];
- if (openCopilotPRs.length > 0) {
- core.info(`Skipping #${issue.number}: has ${openCopilotPRs.length} open PR(s) from Copilot - already being worked on`);
- return false;
- }
-
- return true;
- })
- .map(issue => {
- const issueLabels = issue.labels.map(l => l.name.toLowerCase());
- let score = 0;
-
- // Score based on priority labels (higher score = higher priority)
- if (issueLabels.includes('good first issue') || issueLabels.includes('good-first-issue')) {
- score += 50;
- }
- if (issueLabels.includes('bug')) {
- score += 40;
- }
- if (issueLabels.includes('security')) {
- score += 45;
- }
- if (issueLabels.includes('documentation')) {
- score += 35;
- }
- if (issueLabels.includes('enhancement') || issueLabels.includes('feature')) {
- score += 30;
- }
- if (issueLabels.includes('performance')) {
- score += 25;
- }
- if (issueLabels.includes('tech-debt') || issueLabels.includes('refactoring')) {
- score += 20;
- }
-
- // Bonus for issues with clear labels (any priority label)
- if (issueLabels.some(label => priorityLabels.map(l => l.toLowerCase()).includes(label))) {
- score += 10;
- }
-
- // Age bonus: older issues get slight priority (days old / 10)
- const ageInDays = Math.floor((Date.now() - new Date(issue.created_at)) / (1000 * 60 * 60 * 24));
- score += Math.min(ageInDays / 10, 20); // Cap age bonus at 20 points
-
- return {
- number: issue.number,
- title: issue.title,
- labels: issue.labels.map(l => l.name),
- created_at: issue.created_at,
- score
- };
- })
- .sort((a, b) => b.score - a.score); // Sort by score descending
-
- // Format output
- const issueList = scoredIssues.map(i => {
- const labelStr = i.labels.length > 0 ? ` [${i.labels.join(', ')}]` : '';
- return `#${i.number}: ${i.title}${labelStr} (score: ${i.score.toFixed(1)})`;
- }).join('\n');
-
- const issueNumbers = scoredIssues.map(i => i.number).join(',');
-
- core.info(`Total candidate issues after filtering: ${scoredIssues.length}`);
- if (scoredIssues.length > 0) {
- core.info(`Top candidates:\n${issueList.split('\n').slice(0, 10).join('\n')}`);
- }
-
- core.setOutput('issue_count', scoredIssues.length);
- core.setOutput('issue_numbers', issueNumbers);
- core.setOutput('issue_list', issueList);
-
- if (scoredIssues.length === 0) {
- core.info('🍽️ No suitable candidate issues - the plate is empty!');
- core.setOutput('has_issues', 'false');
- } else {
- core.setOutput('has_issues', 'true');
- }
- } catch (error) {
- core.error(`Error searching for issues: ${error.message}`);
- core.setOutput('issue_count', 0);
- core.setOutput('issue_numbers', '');
- core.setOutput('issue_list', '');
- core.setOutput('has_issues', 'false');
- }
safe-outputs:
assign-to-agent:
@@ -399,7 +398,7 @@ Find up to three issues that need work and assign them to the Copilot coding age
### 1. Review Pre-Searched and Prioritized Issue List
-The issue search has already been performed in a previous job with smart filtering and prioritization:
+The issue search has already been performed in the pre-activation job with smart filtering and prioritization:
**Rate Limiting Protection:**
- 🛡️ **Checks for rate-limited PRs in the last hour** before scheduling new work
@@ -428,12 +427,12 @@ Issues are scored and sorted by priority:
- Has any priority label: +10 points
- Age bonus: +0-20 points (older issues get slight priority)
-**Issue Count**: ${{ needs.search_issues.outputs.issue_count }}
-**Issue Numbers**: ${{ needs.search_issues.outputs.issue_numbers }}
+**Issue Count**: ${{ needs.pre_activation.outputs.issue_count }}
+**Issue Numbers**: ${{ needs.pre_activation.outputs.issue_numbers }}
**Available Issues (sorted by priority score):**
```
-${{ needs.search_issues.outputs.issue_list }}
+${{ needs.pre_activation.outputs.issue_list }}
```
Work with this pre-fetched, filtered, and prioritized list of issues. Do not perform additional searches - the issue numbers are already identified above, sorted from highest to lowest priority.
@@ -459,7 +458,7 @@ For issues with the "task" or "plan" label, check if they are sub-issues linked
### 2. Review the Pre-Filtered Issue List
-The search job has already performed comprehensive filtering, including:
+The pre-activation job has already performed comprehensive filtering, including:
- Issues already assigned to Copilot
- Issues with open PRs linked to them (from any author)
- Issues with closed/merged PRs (treated as complete)
@@ -568,7 +567,7 @@ A successful run means:
## Error Handling
If anything goes wrong or no work can be assigned:
-- **Rate limiting detected**: The workflow automatically skips (no action needed - the search job handles this)
+- **Rate limiting detected**: The workflow automatically skips (no action needed - the pre-activation job handles this)
- **No issues found**: Use the `noop` tool with message: "🍽️ No suitable candidate issues - the plate is empty!"
- **All issues assigned**: Use the `noop` tool with message: "🍽️ All issues are already being worked on!"
- **No suitable separate issues**: Use the `noop` tool explaining which issues were considered and why they couldn't be assigned (e.g., overlapping topics, sibling PRs, etc.)
diff --git a/.github/workflows/issue-triage-agent.lock.yml b/.github/workflows/issue-triage-agent.lock.yml
index 61b52844dc7..d9a9a176e12 100644
--- a/.github/workflows/issue-triage-agent.lock.yml
+++ b/.github/workflows/issue-triage-agent.lock.yml
@@ -62,7 +62,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -77,7 +77,7 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
@@ -87,11 +87,11 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -109,9 +109,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "issue-triage-agent.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -126,15 +126,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, add_labels, missing_tool, missing_data, noop
@@ -168,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -185,9 +186,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -203,10 +204,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -225,11 +226,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -253,10 +254,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: issuetriageagent
outputs:
detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
@@ -277,13 +279,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -297,187 +303,44 @@ jobs:
git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
echo "Git configured with standard GitHub Actions identity"
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":1},"add_labels":{"allowed":["bug","feature","enhancement","documentation","question","help-wanted","good-first-issue"],"max":3},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Add labels to an existing GitHub issue or pull request for categorization and filtering. Labels must already exist in the repository. For creating new issues with labels, use create_issue with the labels property instead. CONSTRAINTS: Only these labels are allowed: [\"bug\" \"feature\" \"enhancement\" \"documentation\" \"question\" \"help-wanted\" \"good-first-issue\"].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). If omitted, adds labels to the issue or PR that triggered this workflow. Only works for issue or pull_request event triggers. For schedule, workflow_dispatch, or other triggers, item_number is required — omitting it will silently skip the label operation.",
- "type": "number"
- },
- "labels": {
- "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "add_labels"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added.",
+ "add_labels": " CONSTRAINTS: Only these labels are allowed: [\"bug\" \"feature\" \"enhancement\" \"documentation\" \"question\" \"help-wanted\" \"good-first-issue\"]."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -575,6 +438,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -599,8 +463,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -611,7 +475,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -619,6 +483,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -636,10 +502,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -651,6 +517,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "issues,labels"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -658,6 +530,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -675,7 +554,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -684,7 +564,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -712,7 +592,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -750,15 +630,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -768,7 +648,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -785,9 +665,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -796,18 +676,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -882,9 +762,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -907,7 +787,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -934,9 +814,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -981,6 +861,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-issue-triage-agent"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -996,7 +878,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1020,9 +902,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1033,9 +915,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1054,9 +936,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1071,9 +953,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1089,6 +971,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/issue-triage-agent"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "issue-triage-agent"
GH_AW_WORKFLOW_NAME: "Issue Triage Agent"
outputs:
@@ -1111,7 +994,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1137,9 +1020,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/jsweep.lock.yml b/.github/workflows/jsweep.lock.yml
index bc095cc57f0..35dfacb9914 100644
--- a/.github/workflows/jsweep.lock.yml
+++ b/.github/workflows/jsweep.lock.yml
@@ -60,7 +60,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -75,18 +75,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -104,9 +104,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "jsweep.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -121,21 +121,21 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -167,6 +167,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -182,9 +183,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -203,10 +204,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -228,11 +229,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -258,10 +259,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: jsweep
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -283,7 +285,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -294,14 +296,18 @@ jobs:
node-version: '20'
package-manager-cache: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Install Node.js dependencies
run: npm install
working-directory: actions/setup/js
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -331,16 +337,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -349,167 +355,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request":{"draft":true,"expires":48,"max":1,"title_prefix":"[jsweep] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[jsweep] \". Labels [\"unbloat\" \"automation\"] will be automatically added. PRs will be created as drafts.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[jsweep] \". Labels [\"unbloat\" \"automation\"] will be automatically added. PRs will be created as drafts."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -606,6 +475,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -630,8 +500,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -642,7 +512,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -650,7 +520,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -668,10 +539,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -679,10 +550,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -690,6 +566,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -698,7 +581,14 @@ jobs:
"args": ["--network", "host"],
"entrypoint": "serena",
"entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -715,7 +605,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -724,7 +615,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -752,7 +643,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -790,15 +681,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -808,7 +699,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -825,9 +716,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -836,18 +727,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -929,9 +820,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -954,7 +845,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -981,9 +872,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1028,6 +919,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-jsweep"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1043,7 +936,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1068,9 +961,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1082,9 +975,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1107,9 +1000,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1125,9 +1018,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1140,9 +1033,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
safe_outputs:
@@ -1159,6 +1052,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/jsweep"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "jsweep-daily"
GH_AW_WORKFLOW_ID: "jsweep"
GH_AW_WORKFLOW_NAME: "jsweep - JavaScript Unbloater"
@@ -1182,7 +1076,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1237,9 +1131,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1256,6 +1150,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: jsweep
steps:
- name: Checkout actions folder
@@ -1268,7 +1163,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/layout-spec-maintainer.lock.yml b/.github/workflows/layout-spec-maintainer.lock.yml
index 52fd536f3ff..8edb02873e4 100644
--- a/.github/workflows/layout-spec-maintainer.lock.yml
+++ b/.github/workflows/layout-spec-maintainer.lock.yml
@@ -61,7 +61,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -76,18 +76,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -105,9 +105,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "layout-spec-maintainer.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -122,20 +122,20 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -167,6 +167,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -180,9 +181,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -198,10 +199,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -220,11 +221,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -249,10 +250,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: layoutspecmaintainer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -274,13 +276,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache configuration from frontmatter processed below
- name: Cache layout spec data
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
@@ -310,16 +316,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -328,167 +334,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request":{"expires":48,"max":1,"title_prefix":"[specs] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[specs] \". Labels [\"documentation\" \"automation\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[specs] \". Labels [\"documentation\" \"automation\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -585,6 +454,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -609,8 +479,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -621,7 +491,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -629,7 +499,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -647,10 +518,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -658,10 +529,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -669,6 +545,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -686,7 +569,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -724,7 +608,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat scratchpad/layout.md)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find .github/workflows -name '\''\'\'''\''*.lock.yml'\''\'\'''\'')'\'' --allow-tool '\''shell(git add:*)'\'' --allow-tool '\''shell(git branch:*)'\'' --allow-tool '\''shell(git checkout:*)'\'' --allow-tool '\''shell(git commit:*)'\'' --allow-tool '\''shell(git diff scratchpad/layout.md)'\'' --allow-tool '\''shell(git merge:*)'\'' --allow-tool '\''shell(git rm:*)'\'' --allow-tool '\''shell(git status)'\'' --allow-tool '\''shell(git switch:*)'\'' --allow-tool '\''shell(grep -r '\''\'\'''\''.*'\''\'\'''\'' pkg/workflow/*.go)'\'' --allow-tool '\''shell(grep -r '\''\'\'''\''.*'\''\'\'''\'' pkg/workflow/js/)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq '\''\'\'''\''.*'\''\'\'''\'' .github/workflows/*.lock.yml)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -752,7 +636,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -790,15 +674,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -808,7 +692,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -820,14 +704,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -836,18 +720,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -923,9 +807,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -948,7 +832,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -975,9 +859,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1021,6 +905,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-layout-spec-maintainer"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1036,7 +922,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1061,9 +947,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1075,9 +961,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1100,9 +986,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1118,9 +1004,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1133,9 +1019,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
safe_outputs:
@@ -1152,6 +1038,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/layout-spec-maintainer"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "layout-spec-maintainer"
GH_AW_WORKFLOW_ID: "layout-spec-maintainer"
GH_AW_WORKFLOW_NAME: "Layout Specification Maintainer"
@@ -1175,7 +1062,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1222,7 +1109,7 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"draft\":false,\"expires\":48,\"labels\":[\"documentation\",\"automation\"],\"max\":1,\"max_patch_size\":1024,\"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\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"title_prefix\":\"[specs] \"},\"missing_data\":{},\"missing_tool\":{}}"
@@ -1230,9 +1117,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml
index d1756a8ea80..d0ffad0c1ed 100644
--- a/.github/workflows/lockfile-stats.lock.yml
+++ b/.github/workflows/lockfile-stats.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "lockfile-stats.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,16 +125,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -168,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -185,9 +186,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -206,10 +207,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -231,11 +232,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -260,10 +261,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: lockfilestats
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -284,16 +286,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -323,9 +329,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -333,7 +339,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -344,152 +350,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -576,6 +460,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -600,8 +485,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -612,7 +497,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -620,7 +505,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -638,19 +524,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -658,6 +549,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -675,7 +573,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -755,7 +654,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash,BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -799,15 +698,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -817,7 +716,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -834,9 +733,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -845,18 +744,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -935,9 +834,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -970,7 +869,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -998,9 +897,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1045,6 +944,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-lockfile-stats"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1060,7 +961,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1084,9 +985,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1097,9 +998,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1120,9 +1021,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1137,9 +1038,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1154,6 +1055,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/lockfile-stats"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "lockfile-stats"
GH_AW_WORKFLOW_NAME: "Lockfile Statistics Analysis Agent"
outputs:
@@ -1174,7 +1076,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1200,9 +1102,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1219,6 +1121,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: lockfilestats
steps:
- name: Checkout actions folder
@@ -1231,7 +1134,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml
index 7075c7aa195..83626cc0327 100644
--- a/.github/workflows/mcp-inspector.lock.yml
+++ b/.github/workflows/mcp-inspector.lock.yml
@@ -79,7 +79,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -94,18 +94,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","containers","node","node-cdns","fonts"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "false"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -123,9 +123,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "mcp-inspector.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -140,16 +140,17 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -183,6 +184,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -244,9 +246,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -265,10 +267,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -290,11 +292,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -320,10 +322,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: mcpinspector
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -345,7 +348,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -392,10 +395,14 @@ jobs:
- name: Setup uv
uses: astral-sh/setup-uv@e06108dd0aef18192324c70427afc47652e63a82 # v7.5.0
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -425,16 +432,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -443,10 +450,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh docker.io/mcp/brave-search ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest mcp/arxiv-mcp-server mcp/ast-grep:latest mcp/context7 mcp/markitdown mcp/memory mcp/notion node:lts-alpine python:alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh docker.io/mcp/brave-search ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest mcp/arxiv-mcp-server mcp/ast-grep:latest mcp/context7 mcp/markitdown mcp/memory mcp/notion node:lts-alpine python:alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -461,192 +468,71 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"notion-add-comment":{"description":"Add a comment to a Notion page","inputs":{"comment":{"default":null,"description":"The comment text to add","required":true,"type":"string"}},"output":"Comment added to Notion successfully!"},"post-to-slack-channel":{"description":"Post a message to a Slack channel. Message must be 200 characters or less. Supports basic Slack markdown: *bold*, _italic_, ~strike~, `code`, ```code block```, \u003equote, and links \u003curl|text\u003e. Requires GH_AW_SLACK_CHANNEL_ID environment variable to be set.","inputs":{"message":{"default":null,"description":"The message to post (max 200 characters, supports Slack markdown)","required":true,"type":"string"}},"output":"Message posted to Slack successfully!"}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\"."
},
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
+ "repo_params": {},
+ "dynamic_tools": [
+ {
+ "description": "Add a comment to a Notion page",
+ "inputSchema": {
+ "additionalProperties": false,
+ "properties": {
+ "comment": {
+ "description": "The comment text to add",
+ "type": "string"
+ }
},
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
+ "required": [
+ "comment"
+ ],
+ "type": "object"
},
- "required": [
- "message"
- ],
- "type": "object"
+ "name": "notion_add_comment"
},
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
+ {
+ "description": "Post a message to a Slack channel. Message must be 200 characters or less. Supports basic Slack markdown: *bold*, _italic_, ~strike~, `code`, ```code block```, \u003equote, and links \u003curl|text\u003e. Requires GH_AW_SLACK_CHANNEL_ID environment variable to be set.",
+ "inputSchema": {
+ "additionalProperties": false,
+ "properties": {
+ "message": {
+ "description": "The message to post (max 200 characters, supports Slack markdown)",
+ "type": "string"
+ }
},
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- },
- {
- "description": "Add a comment to a Notion page",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "comment": {
- "description": "The comment text to add",
- "type": "string"
- }
+ "required": [
+ "message"
+ ],
+ "type": "object"
},
- "required": [
- "comment"
- ],
- "type": "object"
- },
- "name": "notion_add_comment"
- },
- {
- "description": "Post a message to a Slack channel. Message must be 200 characters or less. Supports basic Slack markdown: *bold*, _italic_, ~strike~, `code`, ```code block```, \u003equote, and links \u003curl|text\u003e. Requires GH_AW_SLACK_CHANNEL_ID environment variable to be set.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "message": {
- "description": "The message to post (max 200 characters, supports Slack markdown)",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "post_to_slack_channel"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "name": "post_to_slack_channel"
+ }
+ ]
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -733,6 +619,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -757,8 +644,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -769,7 +656,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -785,7 +672,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NOTION_API_TOKEN: ${{ secrets.NOTION_API_TOKEN }}
@@ -809,10 +697,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e AZURE_CLIENT_ID -e AZURE_CLIENT_SECRET -e AZURE_TENANT_ID -e BRAVE_API_KEY -e CONTEXT7_API_KEY -e DD_API_KEY -e DD_APPLICATION_KEY -e DD_SITE -e NOTION_API_TOKEN -e SENTRY_ACCESS_TOKEN -e SENTRY_HOST -e SENTRY_OPENAI_API_KEY -e TAVILY_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e AZURE_CLIENT_ID -e AZURE_CLIENT_SECRET -e AZURE_TENANT_ID -e BRAVE_API_KEY -e CONTEXT7_API_KEY -e DD_API_KEY -e DD_APPLICATION_KEY -e DD_SITE -e NOTION_API_TOKEN -e SENTRY_ACCESS_TOKEN -e SENTRY_HOST -e SENTRY_OPENAI_API_KEY -e TAVILY_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -825,6 +713,13 @@ jobs:
"GITHUB_TOKEN": "\${GITHUB_TOKEN}",
"GITHUB_ACTOR": "\${GITHUB_ACTOR}",
"GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"arxiv": {
@@ -834,14 +729,28 @@ jobs:
"search_arxiv",
"get_paper_details",
"get_paper_pdf"
- ]
+ ],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"ast-grep": {
"type": "stdio",
"container": "mcp/ast-grep:latest",
"tools": [
"*"
- ]
+ ],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"brave-search": {
"type": "stdio",
@@ -851,6 +760,13 @@ jobs:
],
"env": {
"BRAVE_API_KEY": "\${BRAVE_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"context7": {
@@ -862,6 +778,13 @@ jobs:
],
"env": {
"CONTEXT7_API_KEY": "\${CONTEXT7_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"datadog": {
@@ -882,6 +805,13 @@ jobs:
"DD_API_KEY": "\${DD_API_KEY}",
"DD_APPLICATION_KEY": "\${DD_APPLICATION_KEY}",
"DD_SITE": "\${DD_SITE}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"deepwiki": {
@@ -891,7 +821,14 @@ jobs:
"read_wiki_structure",
"read_wiki_contents",
"ask_question"
- ]
+ ],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"fabric-rti": {
"type": "stdio",
@@ -920,6 +857,13 @@ jobs:
"AZURE_CLIENT_ID": "\${AZURE_CLIENT_ID}",
"AZURE_CLIENT_SECRET": "\${AZURE_CLIENT_SECRET}",
"AZURE_TENANT_ID": "\${AZURE_TENANT_ID}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
@@ -927,10 +871,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"markitdown": {
@@ -938,7 +887,14 @@ jobs:
"container": "mcp/markitdown",
"tools": [
"*"
- ]
+ ],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"memory": {
"type": "stdio",
@@ -952,14 +908,28 @@ jobs:
"retrieve_memory",
"list_memories",
"delete_memory"
- ]
+ ],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"microsoftdocs": {
"type": "http",
"url": "https://learn.microsoft.com/api/mcp",
"tools": [
"*"
- ]
+ ],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"notion": {
"type": "stdio",
@@ -972,6 +942,13 @@ jobs:
],
"env": {
"NOTION_API_TOKEN": "\${NOTION_API_TOKEN}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"safeoutputs": {
@@ -979,6 +956,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"sentry": {
@@ -1009,6 +993,13 @@ jobs:
"OPENAI_API_KEY": "\${SENTRY_OPENAI_API_KEY}",
"SENTRY_ACCESS_TOKEN": "\${SENTRY_ACCESS_TOKEN}",
"SENTRY_HOST": "\${SENTRY_HOST}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -1017,7 +1008,14 @@ jobs:
"args": ["--network", "host"],
"entrypoint": "serena",
"entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"tavily": {
"type": "http",
@@ -1030,6 +1028,13 @@ jobs:
],
"env": {
"TAVILY_API_KEY": "\${TAVILY_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -1047,7 +1052,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -1056,7 +1062,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.docker.com,*.docker.io,*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,auth.docker.io,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,cdn.sheetjs.com,code.jquery.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,data.jsdelivr.com,deb.nodesource.com,deno.land,dl.k8s.io,esm.sh,fonts.googleapis.com,fonts.gstatic.com,gcr.io,get.pnpm.io,ghcr.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,learn.microsoft.com,mcp.datadoghq.com,mcp.deepwiki.com,mcp.tavily.com,mcr.microsoft.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkgs.k8s.io,ppa.launchpad.net,production.cloudflare.docker.com,quay.io,raw.githubusercontent.com,registry.bower.io,registry.hub.docker.com,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.docker.com,*.docker.io,*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,auth.docker.io,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,cdn.sheetjs.com,code.jquery.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,data.jsdelivr.com,deb.nodesource.com,deno.land,dl.k8s.io,esm.sh,fonts.googleapis.com,fonts.gstatic.com,gcr.io,get.pnpm.io,ghcr.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,learn.microsoft.com,mcp.datadoghq.com,mcp.deepwiki.com,mcp.tavily.com,mcr.microsoft.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkgs.k8s.io,ppa.launchpad.net,production.cloudflare.docker.com,quay.io,raw.githubusercontent.com,registry.bower.io,registry.hub.docker.com,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1088,7 +1094,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -1126,15 +1132,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'AZURE_CLIENT_ID,AZURE_CLIENT_SECRET,AZURE_TENANT_ID,BRAVE_API_KEY,CONTEXT7_API_KEY,COPILOT_GITHUB_TOKEN,DD_API_KEY,DD_APPLICATION_KEY,DD_SITE,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,NOTION_API_TOKEN,SENTRY_ACCESS_TOKEN,SENTRY_OPENAI_API_KEY,TAVILY_API_KEY'
@@ -1156,7 +1162,7 @@ jobs:
SECRET_TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1173,9 +1179,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1184,18 +1190,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1276,9 +1282,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1301,7 +1307,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1328,9 +1334,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1377,6 +1383,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-mcp-inspector"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1392,7 +1400,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1416,9 +1424,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1429,9 +1437,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1453,9 +1461,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1470,9 +1478,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
notion_add_comment:
@@ -1488,11 +1496,11 @@ jobs:
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: agent
- path: /opt/gh-aw/safe-jobs/
+ path: ${{ env.GH_AW_HOME }}/safe-jobs/
- name: Setup Safe Job Environment Variables
run: |
- find "/opt/gh-aw/safe-jobs/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/opt/gh-aw/safe-jobs/agent_output.json" >> "$GITHUB_ENV"
+ find "${{ env.GH_AW_HOME }}/safe-jobs/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=${{ env.GH_AW_HOME }}/safe-jobs/agent_output.json" >> "$GITHUB_ENV"
- name: Add comment to Notion page
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
@@ -1616,11 +1624,11 @@ jobs:
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: agent
- path: /opt/gh-aw/safe-jobs/
+ path: ${{ env.GH_AW_HOME }}/safe-jobs/
- name: Setup Safe Job Environment Variables
run: |
- find "/opt/gh-aw/safe-jobs/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/opt/gh-aw/safe-jobs/agent_output.json" >> "$GITHUB_ENV"
+ find "${{ env.GH_AW_HOME }}/safe-jobs/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=${{ env.GH_AW_HOME }}/safe-jobs/agent_output.json" >> "$GITHUB_ENV"
- name: Post message to Slack
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
@@ -1757,6 +1765,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/mcp-inspector"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "mcp-inspector"
GH_AW_WORKFLOW_NAME: "MCP Inspector Agent"
outputs:
@@ -1777,7 +1786,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1804,9 +1813,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1823,6 +1832,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: mcpinspector
steps:
- name: Checkout actions folder
@@ -1835,7 +1845,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/mergefest.lock.yml b/.github/workflows/mergefest.lock.yml
index 5e1c739bf71..6895d4e1389 100644
--- a/.github/workflows/mergefest.lock.yml
+++ b/.github/workflows/mergefest.lock.yml
@@ -53,8 +53,9 @@ jobs:
pull-requests: write
outputs:
body: ${{ steps.sanitized.outputs.body }}
- comment_id: ""
- comment_repo: ""
+ comment_id: ${{ steps.add-comment.outputs.comment-id }}
+ comment_repo: ${{ steps.add-comment.outputs.comment-repo }}
+ comment_url: ${{ steps.add-comment.outputs.comment-url }}
model: ${{ steps.generate_aw_info.outputs.model }}
secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
slash_command: ${{ needs.pre_activation.outputs.matched_command }}
@@ -71,7 +72,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -86,18 +87,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -118,9 +119,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -128,18 +129,30 @@ jobs:
GH_AW_WORKFLOW_FILE: "mergefest.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_WORKFLOW_NAME: "Mergefest"
+ with:
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_workflow_run_comment.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -155,20 +168,20 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: push_to_pull_request_branch, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_push_to_pr_branch.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_push_to_pr_branch.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -200,8 +213,9 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
- cat "/opt/gh-aw/prompts/pr_context_prompt.md"
+ cat "${GH_AW_HOME}/prompts/pr_context_prompt.md"
fi
cat << 'GH_AW_PROMPT_EOF'
@@ -219,9 +233,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -240,10 +254,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_MATCHED_COMMAND: ${{ needs.pre_activation.outputs.matched_command }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -265,11 +279,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -292,10 +306,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: mergefest
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -317,13 +332,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
run: "git config user.name \"github-actions[bot]\"\ngit config user.email \"github-actions[bot]@users.noreply.github.com\"\n\n# Create .gitignore to exclude workflow YAML files\ncat > /tmp/merge-gitignore << 'EOF'\n# Exclude all .yml files in .github/workflows/\n.github/workflows/*.yml\nEOF"
@@ -349,16 +368,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -367,154 +386,28 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Push committed changes to a pull request's branch. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. Changes must be committed locally before calling this tool.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "branch": {
- "description": "Branch name to push changes from. If omitted, uses the current working branch. Only specify if you need to push from a different branch.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Commit message describing the changes. Follow repository commit message conventions (e.g., conventional commits).",
- "type": "string"
- },
- "pull_request_number": {
- "description": "Pull request number to push changes to. This is the numeric ID from the GitHub URL (e.g., 654 in github.com/owner/repo/pull/654). Required when the workflow target is '*' (any PR).",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "push_to_pull_request_branch"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {},
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"missing_data": {
"defaultMax": 20,
@@ -595,6 +488,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -619,8 +513,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -631,7 +525,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -639,7 +533,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -657,10 +552,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -668,10 +563,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "pull_requests,repos"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -679,6 +579,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -696,7 +603,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -744,7 +652,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(git add)'\'' --allow-tool '\''shell(git add:*)'\'' --allow-tool '\''shell(git branch)'\'' --allow-tool '\''shell(git branch:*)'\'' --allow-tool '\''shell(git checkout)'\'' --allow-tool '\''shell(git checkout:*)'\'' --allow-tool '\''shell(git commit)'\'' --allow-tool '\''shell(git commit:*)'\'' --allow-tool '\''shell(git config)'\'' --allow-tool '\''shell(git diff)'\'' --allow-tool '\''shell(git fetch)'\'' --allow-tool '\''shell(git log)'\'' --allow-tool '\''shell(git merge)'\'' --allow-tool '\''shell(git merge:*)'\'' --allow-tool '\''shell(git pull)'\'' --allow-tool '\''shell(git reset)'\'' --allow-tool '\''shell(git rev-parse)'\'' --allow-tool '\''shell(git rm:*)'\'' --allow-tool '\''shell(git status)'\'' --allow-tool '\''shell(git switch:*)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(make fmt)'\'' --allow-tool '\''shell(make lint)'\'' --allow-tool '\''shell(make recompile)'\'' --allow-tool '\''shell(make test-unit)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -772,7 +680,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -810,15 +718,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -828,7 +736,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -846,9 +754,9 @@ jobs:
GH_AW_COMMAND: mergefest
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -857,18 +765,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -944,9 +852,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -969,7 +877,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -996,9 +904,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1041,6 +949,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-mergefest"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1056,7 +966,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1080,9 +990,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1093,9 +1003,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1117,9 +1027,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1134,9 +1044,27 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
+ await main();
+ - name: Update reaction comment with completion status
+ id: conclusion
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ env.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: "Mergefest"
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.agent.outputs.detection_conclusion }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/notify_comment_error.cjs');
await main();
pre_activation:
@@ -1160,7 +1088,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for command workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1169,9 +1097,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check command position
id: check_command_position
@@ -1180,9 +1108,9 @@ jobs:
GH_AW_COMMANDS: "[\"mergefest\"]"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_command_position.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_command_position.cjs');
await main();
safe_outputs:
@@ -1198,6 +1126,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/mergefest"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "mergefest"
GH_AW_WORKFLOW_NAME: "Mergefest"
outputs:
@@ -1220,7 +1149,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1275,9 +1204,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/metrics-collector.lock.yml b/.github/workflows/metrics-collector.lock.yml
index c4bc42d4328..fefe2f71f3f 100644
--- a/.github/workflows/metrics-collector.lock.yml
+++ b/.github/workflows/metrics-collector.lock.yml
@@ -62,7 +62,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -77,18 +77,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -106,9 +106,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "metrics-collector.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -123,15 +123,16 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
The following GitHub context information is available for this workflow:
@@ -162,6 +163,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -176,9 +178,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -201,10 +203,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -230,11 +232,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -257,6 +259,7 @@ jobs:
concurrency:
group: "gh-aw-copilot-${{ github.workflow }}"
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: metricscollector
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -273,7 +276,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -309,7 +312,11 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Repo memory git-based storage configuration from frontmatter processed below
- name: Clone repo-memory branch (default)
env:
@@ -319,7 +326,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -342,16 +349,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -360,10 +367,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -378,12 +385,12 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
@@ -391,7 +398,8 @@ jobs:
- name: Start MCP Gateway
id: start-mcp-gateway
env:
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -410,10 +418,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -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 -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -426,6 +434,13 @@ jobs:
"GITHUB_TOKEN": "\${GITHUB_TOKEN}",
"GITHUB_ACTOR": "\${GITHUB_ACTOR}",
"GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
@@ -433,10 +448,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
}
},
@@ -454,7 +474,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -463,7 +484,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -490,7 +511,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -528,15 +549,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -546,7 +567,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Parse agent logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -554,18 +575,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -625,7 +646,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -634,9 +655,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
push_repo_memory:
@@ -648,6 +669,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -663,7 +686,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -706,8 +729,8 @@ jobs:
FILE_GLOB_FILTER: "metrics/**"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml
index ad1229a9ab4..5d33ccb3448 100644
--- a/.github/workflows/notion-issue-summary.lock.yml
+++ b/.github/workflows/notion-issue-summary.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,18 +81,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -110,9 +110,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "notion-issue-summary.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -128,15 +128,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: missing_tool, missing_data, noop
@@ -170,6 +170,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -187,9 +188,9 @@ jobs:
GH_AW_EXPR_FD3E9604: ${{ github.event.inputs.issue-number }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -206,10 +207,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -229,11 +230,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -256,10 +257,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: notionissuesummary
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -279,13 +281,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -308,16 +314,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -326,135 +332,46 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcp/notion node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcp/notion node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"missing_data":{},"missing_tool":{},"noop":{"max":1},"notion-add-comment":{"description":"Add a comment to a Notion page","inputs":{"comment":{"default":null,"description":"The comment text to add","required":true,"type":"string"}},"output":"Comment added to Notion successfully!"}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {},
+ "repo_params": {},
+ "dynamic_tools": [
+ {
+ "description": "Add a comment to a Notion page",
+ "inputSchema": {
+ "additionalProperties": false,
+ "properties": {
+ "comment": {
+ "description": "The comment text to add",
+ "type": "string"
+ }
},
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- },
- {
- "description": "Add a comment to a Notion page",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "comment": {
- "description": "The comment text to add",
- "type": "string"
- }
+ "required": [
+ "comment"
+ ],
+ "type": "object"
},
- "required": [
- "comment"
- ],
- "type": "object"
- },
- "name": "notion_add_comment"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "name": "notion_add_comment"
+ }
+ ]
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"missing_data": {
"defaultMax": 20,
@@ -515,6 +432,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -539,8 +457,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -551,7 +469,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -559,7 +477,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
NOTION_API_TOKEN: ${{ secrets.NOTION_API_TOKEN }}
run: |
@@ -578,10 +497,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e NOTION_API_TOKEN -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e NOTION_API_TOKEN -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -589,10 +508,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"notion": {
@@ -606,6 +530,13 @@ jobs:
],
"env": {
"NOTION_API_TOKEN": "\${NOTION_API_TOKEN}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"safeoutputs": {
@@ -613,6 +544,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -630,7 +568,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -639,7 +578,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -667,7 +606,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -705,15 +644,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,NOTION_API_TOKEN'
@@ -724,7 +663,7 @@ jobs:
SECRET_NOTION_API_TOKEN: ${{ secrets.NOTION_API_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -741,9 +680,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -752,18 +691,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -809,6 +748,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-notion-issue-summary"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -824,7 +765,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -848,9 +789,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -861,9 +802,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -883,9 +824,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -900,9 +841,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
notion_add_comment:
@@ -918,11 +859,11 @@ jobs:
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: agent
- path: /opt/gh-aw/safe-jobs/
+ path: ${{ env.GH_AW_HOME }}/safe-jobs/
- name: Setup Safe Job Environment Variables
run: |
- find "/opt/gh-aw/safe-jobs/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/opt/gh-aw/safe-jobs/agent_output.json" >> "$GITHUB_ENV"
+ find "${{ env.GH_AW_HOME }}/safe-jobs/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=${{ env.GH_AW_HOME }}/safe-jobs/agent_output.json" >> "$GITHUB_ENV"
- name: Add comment to Notion page
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
@@ -1041,6 +982,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/notion-issue-summary"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "notion-issue-summary"
GH_AW_WORKFLOW_NAME: "Issue Summary to Notion"
outputs:
@@ -1061,7 +1003,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1088,9 +1030,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/org-health-report.lock.yml b/.github/workflows/org-health-report.lock.yml
index 6e569e7ff66..d755c7e8081 100644
--- a/.github/workflows/org-health-report.lock.yml
+++ b/.github/workflows/org-health-report.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,7 +81,7 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
@@ -91,11 +91,11 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -113,9 +113,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "org-health-report.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -130,16 +130,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, upload_asset, missing_tool, missing_data, noop
@@ -175,6 +175,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -197,9 +198,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -218,10 +219,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -243,11 +244,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -274,10 +275,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: orghealthreport
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -299,13 +301,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup Python environment
run: "# Create working directory for Python scripts\nmkdir -p /tmp/gh-aw/python\nmkdir -p /tmp/gh-aw/python/data\nmkdir -p /tmp/gh-aw/python/charts\nmkdir -p /tmp/gh-aw/python/artifacts\n\necho \"Python environment setup complete\"\necho \"Working directory: /tmp/gh-aw/python\"\necho \"Data directory: /tmp/gh-aw/python/data\"\necho \"Charts directory: /tmp/gh-aw/python/charts\"\necho \"Artifacts directory: /tmp/gh-aw/python/artifacts\"\n"
- name: Install Python scientific libraries
@@ -333,7 +339,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -363,185 +369,49 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"reports\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"reports\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -637,6 +507,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -661,8 +532,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -673,7 +544,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -684,6 +555,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -701,10 +574,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -716,6 +589,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "repos,issues,pull_requests,orgs"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -723,6 +602,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -740,7 +626,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -749,7 +636,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,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,files.pythonhosted.org,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,static.crates.io,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,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,files.pythonhosted.org,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,static.crates.io,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -780,7 +667,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -818,15 +705,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -836,7 +723,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -853,9 +740,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -864,18 +751,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -965,9 +852,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -990,7 +877,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1017,9 +904,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1065,6 +952,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-org-health-report"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1080,7 +969,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1104,9 +993,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1117,9 +1006,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1141,9 +1030,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1158,9 +1047,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1175,6 +1064,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/org-health-report"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "org-health-report"
GH_AW_WORKFLOW_NAME: "Organization Health Report"
outputs:
@@ -1195,7 +1085,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1221,9 +1111,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1240,6 +1130,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: orghealthreport
steps:
- name: Checkout actions folder
@@ -1252,7 +1143,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1297,7 +1188,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1352,8 +1243,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml
index 8ef6b299788..5d1c92536e2 100644
--- a/.github/workflows/pdf-summary.lock.yml
+++ b/.github/workflows/pdf-summary.lock.yml
@@ -76,8 +76,9 @@ jobs:
pull-requests: write
outputs:
body: ${{ steps.sanitized.outputs.body }}
- comment_id: ""
- comment_repo: ""
+ comment_id: ${{ steps.add-comment.outputs.comment-id }}
+ comment_repo: ${{ steps.add-comment.outputs.comment-repo }}
+ comment_url: ${{ steps.add-comment.outputs.comment-url }}
model: ${{ steps.generate_aw_info.outputs.model }}
secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
slash_command: ${{ needs.pre_activation.outputs.matched_command }}
@@ -94,7 +95,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -109,18 +110,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -141,9 +142,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -151,18 +152,31 @@ jobs:
GH_AW_WORKFLOW_FILE: "pdf-summary.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_WORKFLOW_NAME: "Resource Summarizer Agent"
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 📄 *Summary compiled by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"📖 Page by page! [{workflow_name}]({run_url}) is reading through this {event_type}...\",\"runSuccess\":\"📚 TL;DR ready! [{workflow_name}]({run_url}) has distilled the essence. Knowledge condensed! ✨\",\"runFailure\":\"📖 Reading interrupted! [{workflow_name}]({run_url}) {status}. The document remains unsummarized...\"}"
+ with:
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_workflow_run_comment.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -182,16 +196,16 @@ jobs:
GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }}
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, create_discussion, missing_tool, missing_data, noop
@@ -225,8 +239,9 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
- cat "/opt/gh-aw/prompts/pr_context_prompt.md"
+ cat "${GH_AW_HOME}/prompts/pr_context_prompt.md"
fi
cat << 'GH_AW_PROMPT_EOF'
@@ -250,9 +265,9 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -278,10 +293,10 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -310,11 +325,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -338,10 +353,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: pdfsummary
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -363,16 +379,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -402,16 +422,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -420,189 +440,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcp/markitdown node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcp/markitdown node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":1},"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added.",
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -707,6 +569,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -731,8 +594,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -743,7 +606,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -751,7 +614,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -769,10 +633,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -780,10 +644,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"markitdown": {
@@ -791,13 +660,27 @@ jobs:
"container": "mcp/markitdown",
"tools": [
"*"
- ]
+ ],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"safeoutputs": {
"type": "http",
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -815,7 +698,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -824,7 +708,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -852,7 +736,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -890,15 +774,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -908,7 +792,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -926,9 +810,9 @@ jobs:
GH_AW_COMMAND: summarize
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -937,18 +821,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1029,9 +913,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1054,7 +938,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1081,9 +965,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1129,6 +1013,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-pdf-summary"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1144,7 +1030,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1168,9 +1054,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1181,9 +1067,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1206,9 +1092,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1223,9 +1109,28 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
+ await main();
+ - name: Update reaction comment with completion status
+ id: conclusion
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ env.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: "Resource Summarizer Agent"
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.agent.outputs.detection_conclusion }}
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 📄 *Summary compiled by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"📖 Page by page! [{workflow_name}]({run_url}) is reading through this {event_type}...\",\"runSuccess\":\"📚 TL;DR ready! [{workflow_name}]({run_url}) has distilled the essence. Knowledge condensed! ✨\",\"runFailure\":\"📖 Reading interrupted! [{workflow_name}]({run_url}) {status}. The document remains unsummarized...\"}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/notify_comment_error.cjs');
await main();
pre_activation:
@@ -1252,7 +1157,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for command workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1261,9 +1166,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check command position
id: check_command_position
@@ -1272,9 +1177,9 @@ jobs:
GH_AW_COMMANDS: "[\"summarize\"]"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_command_position.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_command_position.cjs');
await main();
safe_outputs:
@@ -1290,6 +1195,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/pdf-summary"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 📄 *Summary compiled by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"📖 Page by page! [{workflow_name}]({run_url}) is reading through this {event_type}...\",\"runSuccess\":\"📚 TL;DR ready! [{workflow_name}]({run_url}) has distilled the essence. Knowledge condensed! ✨\",\"runFailure\":\"📖 Reading interrupted! [{workflow_name}]({run_url}) {status}. The document remains unsummarized...\"}"
GH_AW_WORKFLOW_ID: "pdf-summary"
GH_AW_WORKFLOW_NAME: "Resource Summarizer Agent"
@@ -1313,7 +1219,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1339,9 +1245,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1358,6 +1264,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: pdfsummary
steps:
- name: Checkout actions folder
@@ -1370,7 +1277,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml
index 8a54a4883d7..2ac444d3523 100644
--- a/.github/workflows/plan.lock.yml
+++ b/.github/workflows/plan.lock.yml
@@ -23,7 +23,7 @@
#
# Generates project plans and task breakdowns when invoked with /plan command in issues or PRs
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"0557f488754d46db0b535c04267efa16ae72869133b87a4a9a8de87a96067ed3","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"84959bfbc2abf6ea65661e0164519d62d375c38bff49f43d702c29cc56f98af0","strict":true}
name: "Plan Command"
"on":
@@ -58,8 +58,9 @@ jobs:
pull-requests: write
outputs:
body: ${{ steps.sanitized.outputs.body }}
- comment_id: ""
- comment_repo: ""
+ comment_id: ${{ steps.add-comment.outputs.comment-id }}
+ comment_repo: ${{ steps.add-comment.outputs.comment-repo }}
+ comment_url: ${{ steps.add-comment.outputs.comment-url }}
model: ${{ steps.generate_aw_info.outputs.model }}
secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
slash_command: ${{ needs.pre_activation.outputs.matched_command }}
@@ -76,7 +77,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -91,18 +92,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -123,9 +124,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -133,18 +134,30 @@ jobs:
GH_AW_WORKFLOW_FILE: "plan.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_WORKFLOW_NAME: "Plan Command"
+ with:
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_workflow_run_comment.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -161,15 +174,15 @@ jobs:
GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }}
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, close_discussion, missing_tool, missing_data, noop
@@ -203,8 +216,9 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
- cat "/opt/gh-aw/prompts/pr_context_prompt.md"
+ cat "${GH_AW_HOME}/prompts/pr_context_prompt.md"
fi
cat << 'GH_AW_PROMPT_EOF'
@@ -223,9 +237,9 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -245,10 +259,10 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -271,11 +285,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -299,10 +313,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: plan
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -324,13 +339,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -353,217 +372,39 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"close_discussion":{"max":1,"required_category":"Ideas"},"create_issue":{"expires":48,"group":true,"max":5},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 5 issue(s) can be created. Title will be prefixed with \"[plan] \". Labels [\"plan\" \"ai-generated\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Close a GitHub discussion with a resolution comment and optional reason. You can and should always add a comment when closing a discussion to explain the action or provide context. Use this to mark discussions as resolved, answered, or no longer needed. The closing comment should explain why the discussion is being closed. If the discussion is already closed, a comment will still be posted. CONSTRAINTS: Maximum 1 discussion(s) can be closed.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Closing comment explaining why the discussion is being closed and summarizing any resolution or conclusion.",
- "type": "string"
- },
- "discussion_number": {
- "description": "Discussion number to close. This is the numeric ID from the GitHub URL (e.g., 678 in github.com/owner/repo/discussions/678). If omitted, closes the discussion that triggered this workflow (requires a discussion event trigger).",
- "type": [
- "number",
- "string"
- ]
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Resolution reason: RESOLVED (issue addressed), DUPLICATE (discussed elsewhere), OUTDATED (no longer relevant), or ANSWERED (question answered).",
- "enum": [
- "RESOLVED",
- "DUPLICATE",
- "OUTDATED",
- "ANSWERED"
- ],
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "close_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "close_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be closed.",
+ "create_issue": " CONSTRAINTS: Maximum 5 issue(s) can be created. Title will be prefixed with \"[plan] \". Labels [\"plan\" \"ai-generated\" \"cookie\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"close_discussion": {
"defaultMax": 1,
@@ -684,6 +525,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -708,8 +550,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -720,7 +562,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -745,10 +587,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -759,6 +601,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,discussions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "none",
+ "repos": "all"
+ }
}
},
"safeoutputs": {
@@ -766,6 +614,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -783,7 +638,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -792,7 +648,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -820,7 +676,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -858,15 +714,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -876,7 +732,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -894,9 +750,9 @@ jobs:
GH_AW_COMMAND: plan
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -905,18 +761,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -991,9 +847,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1016,7 +872,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1043,9 +899,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1089,6 +945,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-plan"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1104,7 +962,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1128,9 +986,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1141,9 +999,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1163,9 +1021,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1180,9 +1038,27 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
+ await main();
+ - name: Update reaction comment with completion status
+ id: conclusion
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ env.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: "Plan Command"
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.agent.outputs.detection_conclusion }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/notify_comment_error.cjs');
await main();
pre_activation:
@@ -1207,7 +1083,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for command workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1216,9 +1092,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check command position
id: check_command_position
@@ -1227,9 +1103,9 @@ jobs:
GH_AW_COMMANDS: "[\"plan\"]"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_command_position.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_command_position.cjs');
await main();
safe_outputs:
@@ -1244,6 +1120,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/plan"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "plan"
GH_AW_WORKFLOW_NAME: "Plan Command"
outputs:
@@ -1266,7 +1143,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1292,9 +1169,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/plan.md b/.github/workflows/plan.md
index f7f817565be..4ae7615277b 100644
--- a/.github/workflows/plan.md
+++ b/.github/workflows/plan.md
@@ -13,8 +13,9 @@ permissions:
engine: copilot
tools:
github:
- lockdown: false
toolsets: [default, discussions]
+ repos: all
+ min-integrity: none
safe-outputs:
create-issue:
expires: 2d
diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml
index e1e0da53c3c..0a588df37f5 100644
--- a/.github/workflows/poem-bot.lock.yml
+++ b/.github/workflows/poem-bot.lock.yml
@@ -68,8 +68,9 @@ jobs:
pull-requests: write
outputs:
body: ${{ steps.sanitized.outputs.body }}
- comment_id: ""
- comment_repo: ""
+ comment_id: ${{ steps.add-comment.outputs.comment-id }}
+ comment_repo: ${{ steps.add-comment.outputs.comment-repo }}
+ comment_url: ${{ steps.add-comment.outputs.comment-url }}
model: ${{ steps.generate_aw_info.outputs.model }}
secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
slash_command: ${{ needs.pre_activation.outputs.matched_command }}
@@ -86,7 +87,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -101,18 +102,18 @@ jobs:
GH_AW_INFO_STAGED: "true"
GH_AW_INFO_ALLOWED_DOMAINS: '[]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -133,9 +134,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -143,18 +144,31 @@ jobs:
GH_AW_WORKFLOW_FILE: "poem-bot.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_WORKFLOW_NAME: "Poem Bot - A Creative Agentic Workflow"
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🪶 *Verses penned by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🎭 Hear ye! The muse stirs! [{workflow_name}]({run_url}) takes quill in hand for this {event_type}...\",\"runSuccess\":\"🪶 The poem is writ! [{workflow_name}]({run_url}) has composed verses most fair. Applause! 👏\",\"runFailure\":\"🎭 Alas! [{workflow_name}]({run_url}) {status}. The muse has fled, leaving verses unsung...\"}"
+ with:
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_workflow_run_comment.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -172,22 +186,22 @@ jobs:
GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }}
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, create_issue, update_issue, create_discussion, create_agent_session, create_pull_request, close_pull_request, create_pull_request_review_comment, add_labels, push_to_pull_request_branch, upload_asset, link_sub_issue, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
- cat "/opt/gh-aw/prompts/safe_outputs_push_to_pr_branch.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_push_to_pr_branch.md"
cat << 'GH_AW_PROMPT_EOF'
upload_asset: provide a file path; returns a URL; assets are published after the workflow completes (safeoutputs).
@@ -221,8 +235,9 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
- cat "/opt/gh-aw/prompts/pr_context_prompt.md"
+ cat "${GH_AW_HOME}/prompts/pr_context_prompt.md"
fi
cat << 'GH_AW_PROMPT_EOF'
@@ -244,9 +259,9 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -270,10 +285,10 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -300,11 +315,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -327,10 +342,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: poembot
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -352,16 +368,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -391,16 +411,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -409,606 +429,40 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":3,"target":"*"},"add_labels":{"allowed":["poetry","creative","automation","ai-generated","epic","haiku","sonnet","limerick"],"max":5},"close_pull_request":{"max":2,"required_labels":["poetry","automation"],"required_title_prefix":"[🎨 POETRY]","target":"*"},"create_agent_session":{"max":1},"create_discussion":{"expires":24,"max":2},"create_issue":{"expires":48,"group":true,"max":2},"create_missing_tool_issue":{"max":1,"title_prefix":"[missing tool]"},"create_pull_request":{"expires":48,"max":1,"reviewers":["copilot"],"title_prefix":"[🎨 POETRY] "},"create_pull_request_review_comment":{"max":2},"link_sub_issue":{"max":3},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{"max":1},"update_issue":{"max":2},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 2 issue(s) can be created. Title will be prefixed with \"[🎭 POEM-BOT] \". Labels [\"poetry\" \"automation\" \"ai-generated\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Create a GitHub Copilot coding agent session to delegate coding work. Use this when you need another Copilot coding agent to implement code changes, fix bugs, or complete development tasks. The task becomes a new issue that triggers the Copilot coding agent. For non-coding tasks or manual work items, use create_issue instead. CONSTRAINTS: Maximum 1 agent task(s) can be created. Base branch for tasks: \"main\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Clear, detailed task description for the Copilot coding agent. Include specific files to modify, expected behavior, acceptance criteria, and any constraints. The description should be actionable and self-contained.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "create_agent_session"
- },
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 2 discussion(s) can be created. Title will be prefixed with \"[📜 POETRY] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Close a pull request WITHOUT merging, adding a closing comment. You can and should always add a comment when closing a PR to explain the action or provide context. Use this for PRs that should be abandoned, superseded, or closed for other reasons. The closing comment should explain why the PR is being closed. This does NOT merge the changes. If the PR is already closed, a comment will still be posted. CONSTRAINTS: Maximum 2 pull request(s) can be closed. Target: *. Only PRs with labels [poetry automation] can be closed. Only PRs with title prefix \"[🎨 POETRY]\" can be closed.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Closing comment explaining why the PR is being closed without merging (e.g., superseded by another PR, no longer needed, approach rejected).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "pull_request_number": {
- "description": "Pull request number to close. This is the numeric ID from the GitHub URL (e.g., 432 in github.com/owner/repo/pull/432). If omitted, closes the PR that triggered this workflow (requires a pull_request event trigger).",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "close_pull_request"
- },
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 3 comment(s) can be added. Target: *.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[🎨 POETRY] \". Labels [\"poetry\" \"automation\" \"creative-writing\"] will be automatically added. Reviewers [\"copilot\"] will be assigned.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Create a review comment on a specific line of code in a pull request. Use this for inline code review feedback, suggestions, or questions about specific code changes. For general PR comments not tied to specific lines, use add_comment instead. When the workflow is configured with `target: \"*\"`, you must specify `pull_request_number` to indicate which PR to target. CONSTRAINTS: Maximum 2 review comment(s) can be created. Comments will be on the RIGHT side of the diff.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Review comment content in Markdown. Provide specific, actionable feedback about the code at this location.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "line": {
- "description": "Line number for the comment. For single-line comments, this is the target line. For multi-line comments, this is the ending line.",
- "type": [
- "number",
- "string"
- ]
- },
- "path": {
- "description": "File path relative to the repository root (e.g., 'src/auth/login.js'). Must be a file that was changed in the PR.",
- "type": "string"
- },
- "pull_request_number": {
- "description": "Pull request number to add the review comment to. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, adds the comment to the PR that triggered this workflow. Required when the workflow target is '*' (any PR) — omitting it will cause the comment to fail.",
- "type": [
- "number",
- "string"
- ]
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. If omitted, uses the configured target repository. Must be in the allowed-repos list if specified.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "side": {
- "description": "Side of the diff to comment on: RIGHT for the new version (additions), LEFT for the old version (deletions). Defaults to RIGHT.",
- "enum": [
- "LEFT",
- "RIGHT"
- ],
- "type": "string"
- },
- "start_line": {
- "description": "Starting line number for multi-line comments. When set, the comment spans from start_line to line. Omit for single-line comments.",
- "type": [
- "number",
- "string"
- ]
- }
- },
- "required": [
- "path",
- "line",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request_review_comment"
- },
- {
- "description": "Add labels to an existing GitHub issue or pull request for categorization and filtering. Labels must already exist in the repository. For creating new issues with labels, use create_issue with the labels property instead. CONSTRAINTS: Maximum 5 label(s) can be added. Only these labels are allowed: [\"poetry\" \"creative\" \"automation\" \"ai-generated\" \"epic\" \"haiku\" \"sonnet\" \"limerick\"].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). If omitted, adds labels to the issue or PR that triggered this workflow. Only works for issue or pull_request event triggers. For schedule, workflow_dispatch, or other triggers, item_number is required — omitting it will silently skip the label operation.",
- "type": "number"
- },
- "labels": {
- "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "add_labels"
- },
- {
- "description": "Update an existing GitHub issue's title, body, labels, assignees, or milestone WITHOUT closing it. This tool is primarily for editing issue metadata and content. While it supports changing status between 'open' and 'closed', use close_issue instead when you want to close an issue with a closing comment. Body updates support replacing, appending to, prepending content, or updating a per-run \"island\" section. CONSTRAINTS: Maximum 2 issue(s) can be updated. Target: *. Body updates are allowed.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "assignees": {
- "description": "Replace the issue assignees with this list of GitHub usernames (e.g., ['octocat', 'mona']).",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "body": {
- "description": "Issue body content in Markdown. For 'replace', this becomes the entire body. For 'append'/'prepend', this content is added with a separator and an attribution footer. For 'replace-island', only the run-specific section is updated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "issue_number": {
- "description": "Issue number to update. This is the numeric ID from the GitHub URL (e.g., 789 in github.com/owner/repo/issues/789). Required when the workflow target is '*' (any issue).",
- "type": [
- "number",
- "string"
- ]
- },
- "labels": {
- "description": "Replace the issue labels with this list (e.g., ['bug', 'tracking:foo']). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "milestone": {
- "description": "Milestone number to assign (e.g., 1). Use null to clear.",
- "type": [
- "number",
- "string"
- ]
- },
- "operation": {
- "description": "How to update the issue body: 'append' (default - add to end with separator), 'prepend' (add to start with separator), 'replace' (overwrite entire body), or 'replace-island' (update a run-specific section).",
- "enum": [
- "replace",
- "append",
- "prepend",
- "replace-island"
- ],
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "status": {
- "description": "New issue status: 'open' to reopen a closed issue, 'closed' to close an open issue.",
- "enum": [
- "open",
- "closed"
- ],
- "type": "string"
- },
- "title": {
- "description": "New issue title to replace the existing title.",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "update_issue"
- },
- {
- "description": "Push committed changes to a pull request's branch. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. Changes must be committed locally before calling this tool.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "branch": {
- "description": "Branch name to push changes from. If omitted, uses the current working branch. Only specify if you need to push from a different branch.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Commit message describing the changes. Follow repository commit message conventions (e.g., conventional commits).",
- "type": "string"
- },
- "pull_request_number": {
- "description": "Pull request number to push changes to. This is the numeric ID from the GitHub URL (e.g., 654 in github.com/owner/repo/pull/654). Required when the workflow target is '*' (any PR).",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "push_to_pull_request_branch"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Link an issue as a sub-issue of a parent issue. Use this to establish parent-child relationships between issues for better organization and tracking of related work items. CONSTRAINTS: Maximum 3 sub-issue link(s) can be created. The parent issue title must start with \"[🎭 POEM-BOT]\". The sub-issue title must start with \"[🎭 POEM-BOT]\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "parent_issue_number": {
- "description": "The parent issue number to link the sub-issue to. This is the numeric ID from the GitHub URL (e.g., 100 in github.com/owner/repo/issues/100).",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "sub_issue_number": {
- "description": "The issue number to link as a sub-issue of the parent. This is the numeric ID from the GitHub URL (e.g., 101 in github.com/owner/repo/issues/101).",
- "type": [
- "number",
- "string"
- ]
- }
- },
- "required": [
- "parent_issue_number",
- "sub_issue_number"
- ],
- "type": "object"
- },
- "name": "link_sub_issue"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 3 comment(s) can be added. Target: *.",
+ "add_labels": " CONSTRAINTS: Maximum 5 label(s) can be added. Only these labels are allowed: [\"poetry\" \"creative\" \"automation\" \"ai-generated\" \"epic\" \"haiku\" \"sonnet\" \"limerick\"].",
+ "close_pull_request": " CONSTRAINTS: Maximum 2 pull request(s) can be closed. Target: *. Only PRs with labels [poetry automation] can be closed. Only PRs with title prefix \"[🎨 POETRY]\" can be closed.",
+ "create_agent_session": " CONSTRAINTS: Maximum 1 agent task(s) can be created. Base branch for tasks: \"main\".",
+ "create_discussion": " CONSTRAINTS: Maximum 2 discussion(s) can be created. Title will be prefixed with \"[📜 POETRY] \". Discussions will be created in category \"audits\".",
+ "create_issue": " CONSTRAINTS: Maximum 2 issue(s) can be created. Title will be prefixed with \"[🎭 POEM-BOT] \". Labels [\"poetry\" \"automation\" \"ai-generated\"] will be automatically added.",
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[🎨 POETRY] \". Labels [\"poetry\" \"automation\" \"creative-writing\"] will be automatically added. Reviewers [\"copilot\"] will be assigned.",
+ "create_pull_request_review_comment": " CONSTRAINTS: Maximum 2 review comment(s) can be created. Comments will be on the RIGHT side of the diff.",
+ "link_sub_issue": " CONSTRAINTS: Maximum 3 sub-issue link(s) can be created. The parent issue title must start with \"[🎭 POEM-BOT]\". The sub-issue title must start with \"[🎭 POEM-BOT]\".",
+ "update_issue": " CONSTRAINTS: Maximum 2 issue(s) can be updated. Target: *. Body updates are allowed.",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -1372,6 +826,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -1396,8 +851,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -1408,7 +863,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -1419,7 +874,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -1437,10 +893,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -1448,10 +904,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -1459,6 +920,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -1476,7 +944,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -1509,7 +978,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(git add:*)'\'' --allow-tool '\''shell(git branch:*)'\'' --allow-tool '\''shell(git checkout:*)'\'' --allow-tool '\''shell(git commit:*)'\'' --allow-tool '\''shell(git merge:*)'\'' --allow-tool '\''shell(git rm:*)'\'' --allow-tool '\''shell(git status)'\'' --allow-tool '\''shell(git switch:*)'\'' --allow-tool '\''shell(git:*)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1541,7 +1010,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -1579,15 +1048,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -1597,7 +1066,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1615,9 +1084,9 @@ jobs:
GH_AW_COMMAND: poem-bot
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1626,18 +1095,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1729,9 +1198,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1754,7 +1223,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1781,9 +1250,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1830,6 +1299,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-poem-bot"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1845,7 +1316,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1869,9 +1340,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1884,9 +1355,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1911,9 +1382,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1928,9 +1399,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1942,9 +1413,28 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
+ await main();
+ - name: Update reaction comment with completion status
+ id: conclusion
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ env.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: "Poem Bot - A Creative Agentic Workflow"
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.agent.outputs.detection_conclusion }}
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🪶 *Verses penned by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🎭 Hear ye! The muse stirs! [{workflow_name}]({run_url}) takes quill in hand for this {event_type}...\",\"runSuccess\":\"🪶 The poem is writ! [{workflow_name}]({run_url}) has composed verses most fair. Applause! 👏\",\"runFailure\":\"🎭 Alas! [{workflow_name}]({run_url}) {status}. The muse has fled, leaving verses unsung...\"}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/notify_comment_error.cjs');
await main();
pre_activation:
@@ -1968,7 +1458,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for command workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1977,9 +1467,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check command position
id: check_command_position
@@ -1988,9 +1478,9 @@ jobs:
GH_AW_COMMANDS: "[\"poem-bot\"]"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_command_position.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_command_position.cjs');
await main();
safe_outputs:
@@ -2009,6 +1499,7 @@ jobs:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/poem-bot"
GH_AW_ENGINE_ID: "copilot"
GH_AW_ENGINE_MODEL: "gpt-5"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUTS_STAGED: "true"
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🪶 *Verses penned by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🎭 Hear ye! The muse stirs! [{workflow_name}]({run_url}) takes quill in hand for this {event_type}...\",\"runSuccess\":\"🪶 The poem is writ! [{workflow_name}]({run_url}) has composed verses most fair. Applause! 👏\",\"runFailure\":\"🎭 Alas! [{workflow_name}]({run_url}) {status}. The muse has fled, leaving verses unsung...\"}"
GH_AW_WORKFLOW_ID: "poem-bot"
@@ -2041,7 +1532,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -2097,9 +1588,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Create Agent Session
id: create_agent_session
@@ -2111,9 +1602,9 @@ jobs:
with:
github-token: ${{ secrets.COPILOT_GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/create_agent_session.cjs'); await main();
+ const { main } = require(process.env.GH_AW_HOME + '/actions/create_agent_session.cjs'); await main();
update_cache_memory:
needs: agent
@@ -2122,6 +1613,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: poembot
steps:
- name: Checkout actions folder
@@ -2134,7 +1626,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -2179,7 +1671,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -2237,8 +1729,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/portfolio-analyst.lock.yml b/.github/workflows/portfolio-analyst.lock.yml
index 31cec5bea8f..ed4c0b1667b 100644
--- a/.github/workflows/portfolio-analyst.lock.yml
+++ b/.github/workflows/portfolio-analyst.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,18 +81,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -110,9 +110,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "portfolio-analyst.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -127,16 +127,17 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, upload_asset, missing_tool, missing_data, noop
@@ -172,6 +173,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -195,9 +197,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -216,10 +218,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -241,11 +243,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -271,10 +273,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: portfolioanalyst
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -296,7 +299,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -332,7 +335,11 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
- name: Setup Python environment
@@ -366,7 +373,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -396,16 +403,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -414,10 +421,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -432,183 +439,37 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[portfolio] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[portfolio] \". Discussions will be created in category \"audits\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -704,6 +565,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -728,8 +590,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -740,7 +602,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -751,7 +613,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -770,10 +633,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -786,6 +649,13 @@ jobs:
"GITHUB_TOKEN": "\${GITHUB_TOKEN}",
"GITHUB_ACTOR": "\${GITHUB_ACTOR}",
"GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
@@ -793,10 +663,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -804,6 +679,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -821,7 +703,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -830,7 +713,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,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,files.pythonhosted.org,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,static.crates.io,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,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,files.pythonhosted.org,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,static.crates.io,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -861,7 +744,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -899,15 +782,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -917,7 +800,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -934,9 +817,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -945,18 +828,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1046,9 +929,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1071,7 +954,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1098,9 +981,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1146,6 +1029,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-portfolio-analyst"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1161,7 +1046,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1186,9 +1071,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1200,9 +1085,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1225,9 +1110,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1243,9 +1128,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1260,6 +1145,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/portfolio-analyst"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "portfolio-analyst-weekly"
GH_AW_WORKFLOW_ID: "portfolio-analyst"
GH_AW_WORKFLOW_NAME: "Automated Portfolio Analyst"
@@ -1281,7 +1167,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1307,9 +1193,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1326,6 +1212,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: portfolioanalyst
steps:
- name: Checkout actions folder
@@ -1338,7 +1225,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1383,7 +1270,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1439,8 +1326,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/pr-nitpick-reviewer.lock.yml b/.github/workflows/pr-nitpick-reviewer.lock.yml
index 4e694b100ff..ea0e5a16a64 100644
--- a/.github/workflows/pr-nitpick-reviewer.lock.yml
+++ b/.github/workflows/pr-nitpick-reviewer.lock.yml
@@ -86,8 +86,9 @@ jobs:
pull-requests: write
outputs:
body: ${{ steps.sanitized.outputs.body }}
- comment_id: ""
- comment_repo: ""
+ comment_id: ${{ steps.add-comment.outputs.comment-id }}
+ comment_repo: ${{ steps.add-comment.outputs.comment-repo }}
+ comment_url: ${{ steps.add-comment.outputs.comment-url }}
model: ${{ steps.generate_aw_info.outputs.model }}
secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
slash_command: ${{ needs.pre_activation.outputs.matched_command }}
@@ -104,7 +105,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -119,18 +120,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -151,9 +152,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -161,18 +162,31 @@ jobs:
GH_AW_WORKFLOW_FILE: "pr-nitpick-reviewer.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_WORKFLOW_NAME: "PR Nitpick Reviewer 🔍"
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔍 *Meticulously inspected by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔬 Adjusting monocle... [{workflow_name}]({run_url}) is scrutinizing every pixel of this {event_type}...\",\"runSuccess\":\"🔍 Nitpicks catalogued! [{workflow_name}]({run_url}) has documented all the tiny details. Perfection awaits! ✅\",\"runFailure\":\"🔬 Lens cracked! [{workflow_name}]({run_url}) {status}. Some nitpicks remain undetected...\"}"
+ with:
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_workflow_run_comment.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -189,16 +203,16 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, create_pull_request_review_comment, submit_pull_request_review, missing_tool, missing_data, noop
@@ -232,8 +246,9 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
- cat "/opt/gh-aw/prompts/pr_context_prompt.md"
+ cat "${GH_AW_HOME}/prompts/pr_context_prompt.md"
fi
cat << 'GH_AW_PROMPT_EOF'
@@ -255,9 +270,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -280,10 +295,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_MATCHED_COMMAND: ${{ needs.pre_activation.outputs.matched_command }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -309,11 +324,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -336,10 +351,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: prnitpickreviewer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -361,16 +377,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -400,16 +420,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -418,247 +438,32 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"create_pull_request_review_comment":{"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1},"submit_pull_request_review":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[nitpick-report] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Create a review comment on a specific line of code in a pull request. Use this for inline code review feedback, suggestions, or questions about specific code changes. For general PR comments not tied to specific lines, use add_comment instead. When the workflow is configured with `target: \"*\"`, you must specify `pull_request_number` to indicate which PR to target. CONSTRAINTS: Maximum 10 review comment(s) can be created. Comments will be on the RIGHT side of the diff.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Review comment content in Markdown. Provide specific, actionable feedback about the code at this location.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "line": {
- "description": "Line number for the comment. For single-line comments, this is the target line. For multi-line comments, this is the ending line.",
- "type": [
- "number",
- "string"
- ]
- },
- "path": {
- "description": "File path relative to the repository root (e.g., 'src/auth/login.js'). Must be a file that was changed in the PR.",
- "type": "string"
- },
- "pull_request_number": {
- "description": "Pull request number to add the review comment to. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, adds the comment to the PR that triggered this workflow. Required when the workflow target is '*' (any PR) — omitting it will cause the comment to fail.",
- "type": [
- "number",
- "string"
- ]
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. If omitted, uses the configured target repository. Must be in the allowed-repos list if specified.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "side": {
- "description": "Side of the diff to comment on: RIGHT for the new version (additions), LEFT for the old version (deletions). Defaults to RIGHT.",
- "enum": [
- "LEFT",
- "RIGHT"
- ],
- "type": "string"
- },
- "start_line": {
- "description": "Starting line number for multi-line comments. When set, the comment spans from start_line to line. Omit for single-line comments.",
- "type": [
- "number",
- "string"
- ]
- }
- },
- "required": [
- "path",
- "line",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request_review_comment"
- },
- {
- "description": "Submit a pull request review with a status decision. All create_pull_request_review_comment outputs are automatically collected and included as inline comments in this review. Use APPROVE to approve the PR, REQUEST_CHANGES to request changes, or COMMENT for general feedback without a decision. If you don't call this tool, review comments are still submitted as a COMMENT review. CONSTRAINTS: Maximum 1 review(s) can be submitted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Overall review summary in Markdown. Provide a high-level assessment of the changes. Required for REQUEST_CHANGES; optional for APPROVE and COMMENT.",
- "type": "string"
- },
- "event": {
- "description": "Review decision: APPROVE to approve the pull request, REQUEST_CHANGES to formally request changes before merging, or COMMENT for general feedback without a formal decision. Defaults to COMMENT when omitted.",
- "enum": [
- "APPROVE",
- "REQUEST_CHANGES",
- "COMMENT"
- ],
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "submit_pull_request_review"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[nitpick-report] \". Discussions will be created in category \"audits\".",
+ "create_pull_request_review_comment": " CONSTRAINTS: Maximum 10 review comment(s) can be created. Comments will be on the RIGHT side of the diff.",
+ "submit_pull_request_review": " CONSTRAINTS: Maximum 1 review(s) can be submitted."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -800,6 +605,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -824,8 +630,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -836,7 +642,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -844,7 +650,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -862,10 +669,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -873,10 +680,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "pull_requests,repos"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -884,6 +696,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -901,7 +720,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -910,7 +730,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -938,7 +758,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -976,15 +796,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -994,7 +814,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1012,9 +832,9 @@ jobs:
GH_AW_COMMAND: nit
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1023,18 +843,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1115,9 +935,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1140,7 +960,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1167,9 +987,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1215,6 +1035,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-pr-nitpick-reviewer"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1230,7 +1052,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1254,9 +1076,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1267,9 +1089,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1292,9 +1114,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1309,9 +1131,28 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
+ await main();
+ - name: Update reaction comment with completion status
+ id: conclusion
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ env.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: "PR Nitpick Reviewer 🔍"
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.agent.outputs.detection_conclusion }}
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔍 *Meticulously inspected by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔬 Adjusting monocle... [{workflow_name}]({run_url}) is scrutinizing every pixel of this {event_type}...\",\"runSuccess\":\"🔍 Nitpicks catalogued! [{workflow_name}]({run_url}) has documented all the tiny details. Perfection awaits! ✅\",\"runFailure\":\"🔬 Lens cracked! [{workflow_name}]({run_url}) {status}. Some nitpicks remain undetected...\"}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/notify_comment_error.cjs');
await main();
pre_activation:
@@ -1342,7 +1183,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for command workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1351,9 +1192,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check command position
id: check_command_position
@@ -1362,9 +1203,9 @@ jobs:
GH_AW_COMMANDS: "[\"nit\"]"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_command_position.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_command_position.cjs');
await main();
safe_outputs:
@@ -1380,6 +1221,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/pr-nitpick-reviewer"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔍 *Meticulously inspected by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔬 Adjusting monocle... [{workflow_name}]({run_url}) is scrutinizing every pixel of this {event_type}...\",\"runSuccess\":\"🔍 Nitpicks catalogued! [{workflow_name}]({run_url}) has documented all the tiny details. Perfection awaits! ✅\",\"runFailure\":\"🔬 Lens cracked! [{workflow_name}]({run_url}) {status}. Some nitpicks remain undetected...\"}"
GH_AW_WORKFLOW_ID: "pr-nitpick-reviewer"
GH_AW_WORKFLOW_NAME: "PR Nitpick Reviewer 🔍"
@@ -1401,7 +1243,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1427,9 +1269,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1446,6 +1288,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: prnitpickreviewer
steps:
- name: Checkout actions folder
@@ -1458,7 +1301,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml
index b126b751e2f..a45aff99347 100644
--- a/.github/workflows/pr-triage-agent.lock.yml
+++ b/.github/workflows/pr-triage-agent.lock.yml
@@ -59,7 +59,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -74,7 +74,7 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
@@ -84,11 +84,11 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -106,9 +106,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "pr-triage-agent.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -124,16 +124,16 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, create_issue, add_labels, missing_tool, missing_data, noop
@@ -167,6 +167,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -182,9 +183,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -206,10 +207,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -234,11 +235,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -263,10 +264,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: prtriageagent
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -288,13 +290,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Repo memory git-based storage configuration from frontmatter processed below
- name: Clone repo-memory branch (default)
env:
@@ -304,7 +310,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -327,256 +333,50 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":50},"add_labels":{"max":100},"create_issue":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[PR Triage Report] \".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 50 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Add labels to an existing GitHub issue or pull request for categorization and filtering. Labels must already exist in the repository. For creating new issues with labels, use create_issue with the labels property instead. CONSTRAINTS: Maximum 100 label(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). If omitted, adds labels to the issue or PR that triggered this workflow. Only works for issue or pull_request event triggers. For schedule, workflow_dispatch, or other triggers, item_number is required — omitting it will silently skip the label operation.",
- "type": "number"
- },
- "labels": {
- "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "add_labels"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 50 comment(s) can be added.",
+ "add_labels": " CONSTRAINTS: Maximum 100 label(s) can be added.",
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[PR Triage Report] \"."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -707,6 +507,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -731,8 +532,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -743,7 +544,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -751,6 +552,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -768,10 +571,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -783,6 +586,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "pull_requests,repos,issues,labels"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -790,6 +599,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -807,7 +623,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -816,7 +633,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -844,7 +661,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -882,15 +699,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -900,7 +717,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -917,9 +734,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -928,18 +745,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1023,9 +840,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1048,7 +865,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1075,9 +892,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1123,6 +940,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-pr-triage-agent"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1138,7 +957,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1162,9 +981,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1175,9 +994,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1202,9 +1021,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1219,9 +1038,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1233,6 +1052,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1248,7 +1069,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1291,9 +1112,9 @@ jobs:
FILE_GLOB_FILTER: "**"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1309,6 +1130,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/pr-triage-agent"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"runStarted\":\"🔍 Starting PR triage analysis... [{workflow_name}]({run_url}) is categorizing and prioritizing agent-created PRs\",\"runSuccess\":\"✅ PR triage complete! [{workflow_name}]({run_url}) has analyzed and categorized PRs. Check the issue for detailed report.\",\"runFailure\":\"❌ PR triage failed! [{workflow_name}]({run_url}) {status}. Some PRs may not be triaged.\"}"
GH_AW_WORKFLOW_ID: "pr-triage-agent"
GH_AW_WORKFLOW_NAME: "PR Triage Agent"
@@ -1334,7 +1156,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1360,9 +1182,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/prompt-clustering-analysis.lock.yml b/.github/workflows/prompt-clustering-analysis.lock.yml
index aa0092a8693..a6279fdd853 100644
--- a/.github/workflows/prompt-clustering-analysis.lock.yml
+++ b/.github/workflows/prompt-clustering-analysis.lock.yml
@@ -27,10 +27,11 @@
# Imports:
# - shared/copilot-pr-data-fetch.md
# - shared/jqschema.md
+# - shared/python-nlp.md
# - shared/reporting.md
# - shared/trending-charts-simple.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"711d989f13320ca6c9165974764c245b845badcef5a8e6cd3b6ec8ee0373b2db","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"96bf1a439f0d9f0ecdb73ad42681fa771364b0c93b9b2e666be8ac3411c0f6f8","strict":true}
name: "Copilot Agent Prompt Clustering Analysis"
"on":
@@ -69,7 +70,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -84,18 +85,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -113,9 +114,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "prompt-clustering-analysis.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -130,16 +131,17 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -173,6 +175,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -186,6 +189,9 @@ jobs:
{{#runtime-import .github/workflows/shared/copilot-pr-data-fetch.md}}
GH_AW_PROMPT_EOF
cat << 'GH_AW_PROMPT_EOF'
+ {{#runtime-import .github/workflows/shared/python-nlp.md}}
+ GH_AW_PROMPT_EOF
+ cat << 'GH_AW_PROMPT_EOF'
{{#runtime-import .github/workflows/shared/trending-charts-simple.md}}
GH_AW_PROMPT_EOF
cat << 'GH_AW_PROMPT_EOF'
@@ -199,9 +205,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -220,10 +226,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -245,11 +251,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -275,10 +281,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: promptclusteringanalysis
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -299,7 +306,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -335,7 +342,11 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
- env:
@@ -343,6 +354,8 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
name: Fetch Copilot PR data
run: "# Create output directories\nmkdir -p /tmp/gh-aw/pr-data\nmkdir -p /tmp/gh-aw/cache-memory\n\n# Get today's date for cache identification\nTODAY=$(date '+%Y-%m-%d')\nCACHE_DIR=\"/tmp/gh-aw/cache-memory\"\n\n# Check if cached data exists from today\nif [ -f \"$CACHE_DIR/copilot-prs-${TODAY}.json\" ] && [ -s \"$CACHE_DIR/copilot-prs-${TODAY}.json\" ]; then\n echo \"✓ Found cached PR data from ${TODAY}\"\n cp \"$CACHE_DIR/copilot-prs-${TODAY}.json\" /tmp/gh-aw/pr-data/copilot-prs.json\n \n # Regenerate schema if missing\n if [ ! -f \"$CACHE_DIR/copilot-prs-${TODAY}-schema.json\" ]; then\n /tmp/gh-aw/jqschema.sh < /tmp/gh-aw/pr-data/copilot-prs.json > \"$CACHE_DIR/copilot-prs-${TODAY}-schema.json\"\n fi\n cp \"$CACHE_DIR/copilot-prs-${TODAY}-schema.json\" /tmp/gh-aw/pr-data/copilot-prs-schema.json\n \n echo \"Using cached data from ${TODAY}\"\n echo \"Total PRs in cache: $(jq 'length' /tmp/gh-aw/pr-data/copilot-prs.json)\"\nelse\n echo \"⬇ Downloading fresh PR data...\"\n \n # Calculate date 30 days ago\n DATE_30_DAYS_AGO=$(date -d '30 days ago' '+%Y-%m-%d' 2>/dev/null || date -v-30d '+%Y-%m-%d')\n\n # Search for PRs from copilot/* branches in the last 30 days using gh CLI\n # Using branch prefix search (head:copilot/) instead of author for reliability\n echo \"Fetching Copilot PRs from the last 30 days...\"\n gh pr list --repo ${{ github.repository }} \\\n --search \"head:copilot/ created:>=${DATE_30_DAYS_AGO}\" \\\n --state all \\\n --json number,title,author,headRefName,createdAt,state,url,body,labels,updatedAt,closedAt,mergedAt \\\n --limit 1000 \\\n > /tmp/gh-aw/pr-data/copilot-prs.json\n\n # Generate schema for reference\n /tmp/gh-aw/jqschema.sh < /tmp/gh-aw/pr-data/copilot-prs.json > /tmp/gh-aw/pr-data/copilot-prs-schema.json\n\n # Store in cache with today's date\n cp /tmp/gh-aw/pr-data/copilot-prs.json \"$CACHE_DIR/copilot-prs-${TODAY}.json\"\n cp /tmp/gh-aw/pr-data/copilot-prs-schema.json \"$CACHE_DIR/copilot-prs-${TODAY}-schema.json\"\n\n echo \"✓ PR data saved to cache: copilot-prs-${TODAY}.json\"\n echo \"Total PRs found: $(jq 'length' /tmp/gh-aw/pr-data/copilot-prs.json)\"\nfi\n\n# Always ensure data is available at expected locations for backward compatibility\necho \"PR data available at: /tmp/gh-aw/pr-data/copilot-prs.json\"\necho \"Schema available at: /tmp/gh-aw/pr-data/copilot-prs-schema.json\""
+ - name: Setup Python NLP environment
+ run: "mkdir -p /tmp/gh-aw/python/{data,charts,artifacts}\npip install --user --quiet nltk scikit-learn textblob wordcloud\n\n# Download required NLTK corpora\npython3 -c \"\nimport nltk\nfor corpus in ['punkt_tab', 'stopwords', 'vader_lexicon', 'averaged_perceptron_tagger_eng']:\n nltk.download(corpus, quiet=True)\nprint('NLTK corpora ready')\n\"\n\npython3 -c \"import sklearn; print(f'scikit-learn {sklearn.__version__}')\""
- name: Setup Python environment
run: |
mkdir -p /tmp/gh-aw/python/{data,charts,artifacts}
@@ -365,9 +378,6 @@ jobs:
/tmp/gh-aw/python/*.py
/tmp/gh-aw/python/data/*
retention-days: 30
- - name: Install additional ML libraries
- run: |
- pip3 install --user scikit-learn nltk
- env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -388,7 +398,7 @@ jobs:
restore-keys: prompt-clustering-cache-
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -418,9 +428,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -428,7 +438,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -439,10 +449,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -457,158 +467,36 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[prompt-clustering] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[prompt-clustering] \". Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -695,6 +583,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -719,8 +608,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -731,7 +620,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -739,7 +628,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -758,9 +648,9 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -772,16 +662,28 @@ jobs:
"GITHUB_TOKEN": "$GITHUB_TOKEN",
"GITHUB_ACTOR": "$GITHUB_ACTOR",
"GITHUB_REPOSITORY": "$GITHUB_REPOSITORY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "repos,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -789,6 +691,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -806,7 +715,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -886,7 +796,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash,BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -930,15 +840,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -948,7 +858,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -960,14 +870,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -976,18 +886,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1066,9 +976,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1101,7 +1011,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1129,9 +1039,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1176,6 +1086,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-prompt-clustering-analysis"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1191,7 +1103,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1215,9 +1127,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1228,9 +1140,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1251,9 +1163,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1268,9 +1180,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1285,6 +1197,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/prompt-clustering-analysis"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "prompt-clustering-analysis"
GH_AW_WORKFLOW_NAME: "Copilot Agent Prompt Clustering Analysis"
outputs:
@@ -1305,7 +1218,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1324,16 +1237,16 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,cdn.playwright.dev,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,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[prompt-clustering] \"},\"missing_data\":{},\"missing_tool\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1350,6 +1263,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: promptclusteringanalysis
steps:
- name: Checkout actions folder
@@ -1362,7 +1276,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/prompt-clustering-analysis.md b/.github/workflows/prompt-clustering-analysis.md
index e4cde4585eb..0e5521933d9 100644
--- a/.github/workflows/prompt-clustering-analysis.md
+++ b/.github/workflows/prompt-clustering-analysis.md
@@ -31,6 +31,7 @@ imports:
- shared/jqschema.md
- shared/reporting.md
- shared/copilot-pr-data-fetch.md
+ - shared/python-nlp.md
- shared/trending-charts-simple.md
cache:
@@ -48,10 +49,6 @@ tools:
bash: ["*"]
steps:
- - name: Install additional ML libraries
- run: |
- pip3 install --user scikit-learn nltk
-
- name: Download full PR data with comments and reviews
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/python-data-charts.lock.yml b/.github/workflows/python-data-charts.lock.yml
index 3fd36377316..086e58f0dc9 100644
--- a/.github/workflows/python-data-charts.lock.yml
+++ b/.github/workflows/python-data-charts.lock.yml
@@ -63,7 +63,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -78,18 +78,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -107,9 +107,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "python-data-charts.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -124,16 +124,17 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, upload_asset, missing_tool, missing_data, noop
@@ -169,6 +170,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -193,9 +195,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -214,10 +216,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -239,11 +241,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -267,10 +269,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: pythondatacharts
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -292,7 +295,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -328,7 +331,11 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup Python environment
run: "# Create working directory for Python scripts\nmkdir -p /tmp/gh-aw/python\nmkdir -p /tmp/gh-aw/python/data\nmkdir -p /tmp/gh-aw/python/charts\nmkdir -p /tmp/gh-aw/python/artifacts\n\necho \"Python environment setup complete\"\necho \"Working directory: /tmp/gh-aw/python\"\necho \"Data directory: /tmp/gh-aw/python/data\"\necho \"Charts directory: /tmp/gh-aw/python/charts\"\necho \"Artifacts directory: /tmp/gh-aw/python/artifacts\"\n"
- name: Install Python scientific libraries
@@ -354,7 +361,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -384,16 +391,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -402,10 +409,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -420,183 +427,37 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"artifacts\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"artifacts\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -692,6 +553,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -716,8 +578,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -728,7 +590,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -739,7 +601,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -758,10 +621,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -774,6 +637,13 @@ jobs:
"GITHUB_TOKEN": "\${GITHUB_TOKEN}",
"GITHUB_ACTOR": "\${GITHUB_ACTOR}",
"GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
@@ -781,10 +651,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -792,6 +667,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -809,7 +691,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -818,7 +701,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,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,files.pythonhosted.org,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,static.crates.io,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,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,files.pythonhosted.org,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,static.crates.io,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -849,7 +732,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -887,15 +770,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -905,7 +788,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -922,9 +805,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -933,18 +816,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1034,9 +917,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1059,7 +942,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1086,9 +969,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1134,6 +1017,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-python-data-charts"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1149,7 +1034,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1173,9 +1058,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1186,9 +1071,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1210,9 +1095,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1227,9 +1112,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1244,6 +1129,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/python-data-charts"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "python-data-charts"
GH_AW_WORKFLOW_NAME: "Python Data Visualization Generator"
outputs:
@@ -1264,7 +1150,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1290,9 +1176,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1309,6 +1195,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: pythondatacharts
steps:
- name: Checkout actions folder
@@ -1321,7 +1208,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1366,7 +1253,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1421,8 +1308,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml
index e221c0d5a43..33864c4771f 100644
--- a/.github/workflows/q.lock.yml
+++ b/.github/workflows/q.lock.yml
@@ -113,7 +113,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -128,18 +128,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -160,9 +160,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -170,18 +170,18 @@ jobs:
GH_AW_WORKFLOW_FILE: "q.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/compute_text.cjs');
await main();
- name: Add comment with workflow run link
id: add-comment
@@ -192,9 +192,9 @@ jobs:
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🎩 *Equipped by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔧 Pay attention, 007! [{workflow_name}]({run_url}) is preparing your gadgets for this {event_type}...\",\"runSuccess\":\"🎩 Mission equipment ready! [{workflow_name}]({run_url}) has optimized your workflow. Use wisely, 007! 🔫\",\"runFailure\":\"🔧 Technical difficulties! [{workflow_name}]({run_url}) {status}. Even Q Branch has bad days...\"}"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_workflow_run_comment.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_workflow_run_comment.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -212,21 +212,22 @@ jobs:
GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }}
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -258,8 +259,9 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
- cat "/opt/gh-aw/prompts/pr_context_prompt.md"
+ cat "${GH_AW_HOME}/prompts/pr_context_prompt.md"
fi
cat << 'GH_AW_PROMPT_EOF'
@@ -284,9 +286,9 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -310,10 +312,10 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -340,11 +342,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -369,10 +371,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: q
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -394,7 +397,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -430,10 +433,14 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -463,16 +470,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -481,10 +488,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -499,210 +506,37 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":1},"create_pull_request":{"expires":48,"max":1,"reviewers":["copilot"],"title_prefix":"[q] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[q] \". Labels [\"automation\" \"workflow-optimization\"] will be automatically added. Reviewers [\"copilot\"] will be assigned.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added.",
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[q] \". Labels [\"automation\" \"workflow-optimization\"] will be automatically added. Reviewers [\"copilot\"] will be assigned."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -817,6 +651,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -841,8 +676,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -853,7 +688,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -861,7 +696,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -880,10 +716,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -896,6 +732,13 @@ jobs:
"GITHUB_TOKEN": "\${GITHUB_TOKEN}",
"GITHUB_ACTOR": "\${GITHUB_ACTOR}",
"GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
@@ -903,10 +746,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,actions,discussions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -914,6 +762,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -922,7 +777,14 @@ jobs:
"args": ["--network", "host"],
"entrypoint": "serena",
"entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -939,7 +801,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -948,7 +811,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -976,7 +839,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -1014,15 +877,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -1032,7 +895,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1050,9 +913,9 @@ jobs:
GH_AW_COMMAND: q
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1061,18 +924,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1154,9 +1017,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1179,7 +1042,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1206,9 +1069,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1254,6 +1117,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-q"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1269,7 +1134,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1293,9 +1158,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1306,9 +1171,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1331,9 +1196,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1348,9 +1213,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1362,9 +1227,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
- name: Update reaction comment with completion status
id: conclusion
@@ -1381,9 +1246,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/notify_comment_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/notify_comment_error.cjs');
await main();
pre_activation:
@@ -1420,7 +1285,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for command workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1429,9 +1294,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check command position
id: check_command_position
@@ -1440,9 +1305,9 @@ jobs:
GH_AW_COMMANDS: "[\"q\"]"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_command_position.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_command_position.cjs');
await main();
safe_outputs:
@@ -1460,6 +1325,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/q"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🎩 *Equipped by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔧 Pay attention, 007! [{workflow_name}]({run_url}) is preparing your gadgets for this {event_type}...\",\"runSuccess\":\"🎩 Mission equipment ready! [{workflow_name}]({run_url}) has optimized your workflow. Use wisely, 007! 🔫\",\"runFailure\":\"🔧 Technical difficulties! [{workflow_name}]({run_url}) {status}. Even Q Branch has bad days...\"}"
GH_AW_WORKFLOW_ID: "q"
GH_AW_WORKFLOW_NAME: "Q"
@@ -1485,7 +1351,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1540,9 +1406,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1559,6 +1425,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: q
steps:
- name: Checkout actions folder
@@ -1571,7 +1438,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/refiner.lock.yml b/.github/workflows/refiner.lock.yml
index d6d6ee5109b..a1918d28e30 100644
--- a/.github/workflows/refiner.lock.yml
+++ b/.github/workflows/refiner.lock.yml
@@ -75,7 +75,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -90,7 +90,7 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
@@ -100,11 +100,11 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -122,18 +122,18 @@ jobs:
GH_AW_WORKFLOW_FILE: "refiner.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/compute_text.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -149,20 +149,20 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -194,6 +194,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -211,9 +212,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -231,10 +232,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -255,11 +256,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -282,10 +283,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: refiner
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -307,13 +309,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -336,212 +342,49 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":1},"create_pull_request":{"max":1,"reviewers":["copilot"],"title_prefix":"[refiner] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[refiner] \". Labels [\"automation\" \"refine-improvements\"] will be automatically added. Reviewers [\"copilot\"] will be assigned.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added.",
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[refiner] \". Labels [\"automation\" \"refine-improvements\"] will be automatically added. Reviewers [\"copilot\"] will be assigned."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -656,6 +499,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -680,8 +524,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -692,7 +536,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -700,6 +544,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -717,10 +563,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -732,6 +578,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "pull_requests,repos,issues"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -739,6 +591,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -756,7 +615,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -765,7 +625,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -793,7 +653,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -831,15 +691,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -849,7 +709,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -866,9 +726,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -877,18 +737,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -964,9 +824,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -989,7 +849,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1016,9 +876,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1063,6 +923,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-refiner"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1078,7 +940,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1102,9 +964,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1115,9 +977,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1140,9 +1002,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1157,9 +1019,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1171,9 +1033,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
pre_activation:
@@ -1197,7 +1059,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1206,9 +1068,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
safe_outputs:
@@ -1226,6 +1088,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/refiner"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"runStarted\":\"🔍 Starting code refinement... [{workflow_name}]({run_url}) is analyzing PR #${{ github.event.pull_request.number }} for style alignment and security issues\",\"runSuccess\":\"✅ Refinement complete! [{workflow_name}]({run_url}) has created a PR with improvements for PR #${{ github.event.pull_request.number }}\",\"runFailure\":\"❌ Refinement failed! [{workflow_name}]({run_url}) {status} while processing PR #${{ github.event.pull_request.number }}\"}"
GH_AW_WORKFLOW_ID: "refiner"
GH_AW_WORKFLOW_NAME: "Code Refiner"
@@ -1251,7 +1114,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1306,9 +1169,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml
index abf0979de90..817e35bb3e8 100644
--- a/.github/workflows/release.lock.yml
+++ b/.github/workflows/release.lock.yml
@@ -72,7 +72,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -87,18 +87,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","node","github.github.com"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -116,9 +116,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "release.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -133,15 +133,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: update_release, missing_tool, missing_data, noop
@@ -175,6 +175,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -189,9 +190,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -208,10 +209,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -231,11 +232,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -262,10 +263,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: release
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -287,13 +289,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_ID: ${{ needs.release.outputs.release_id }}
@@ -323,16 +329,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -341,158 +347,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"missing_data":{},"missing_tool":{},"noop":{"max":1},"update_release":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Update a GitHub release description by replacing, appending to, or prepending to the existing content. Use this to add release notes, changelogs, or additional information to an existing release. CONSTRAINTS: Maximum 1 release(s) can be updated.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Release body content in Markdown. For 'replace', this becomes the entire release body. For 'append'/'prepend', this is added with a separator.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "operation": {
- "description": "How to update the release body: 'replace' (completely overwrite), 'append' (add to end with separator), or 'prepend' (add to start with separator).",
- "enum": [
- "replace",
- "append",
- "prepend"
- ],
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tag": {
- "description": "Release tag name (e.g., 'v1.0.0'). REQUIRED - must be provided explicitly as the tag cannot always be inferred from event context.",
- "type": "string"
- }
- },
- "required": [
- "tag",
- "operation",
- "body"
- ],
- "type": "object"
- },
- "name": "update_release"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "update_release": " CONSTRAINTS: Maximum 1 release(s) can be updated."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"missing_data": {
"defaultMax": 20,
@@ -578,6 +456,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -602,8 +481,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -614,7 +493,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -622,7 +501,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -640,10 +520,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -651,10 +531,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -662,6 +547,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -679,7 +571,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -688,7 +581,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,github.github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,github.github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -716,7 +609,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -754,15 +647,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -772,7 +665,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -789,9 +682,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -800,18 +693,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -886,9 +779,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -911,7 +804,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -938,9 +831,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -982,6 +875,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-release"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -997,7 +892,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1021,9 +916,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1034,9 +929,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1056,9 +951,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1073,9 +968,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
config:
@@ -1204,7 +1099,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1213,9 +1108,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
push_tag:
@@ -1433,6 +1328,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/release"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "release"
GH_AW_WORKFLOW_NAME: "Release"
outputs:
@@ -1453,7 +1349,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1479,9 +1375,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/repo-audit-analyzer.lock.yml b/.github/workflows/repo-audit-analyzer.lock.yml
index 91d929a6586..5851aff1782 100644
--- a/.github/workflows/repo-audit-analyzer.lock.yml
+++ b/.github/workflows/repo-audit-analyzer.lock.yml
@@ -67,7 +67,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -82,18 +82,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -111,9 +111,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "repo-audit-analyzer.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -129,16 +129,16 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_INPUTS_REPOSITORY: ${{ inputs.repository }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt_multi.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt_multi.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -172,6 +172,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -189,9 +190,9 @@ jobs:
GH_AW_INPUTS_REPOSITORY: ${{ inputs.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -211,10 +212,10 @@ jobs:
GH_AW_INPUTS_REPOSITORY: ${{ inputs.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -237,11 +238,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -265,10 +266,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: repoauditanalyzer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -290,13 +292,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory (repo-audits)
run: |
@@ -330,16 +336,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -348,152 +354,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"create_missing_tool_issue":{"labels":["cookie"],"max":1,"title_prefix":"[missing tool]"},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -580,6 +464,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -604,8 +489,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -616,7 +501,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -624,7 +509,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -642,10 +528,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -653,10 +539,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -664,6 +555,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -681,7 +579,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -690,7 +589,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory-repo-audits/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -718,7 +617,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -756,15 +655,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -774,7 +673,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -791,9 +690,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -802,18 +701,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -894,9 +793,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -919,7 +818,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -946,9 +845,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -993,6 +892,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-repo-audit-analyzer"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1008,7 +909,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1032,9 +933,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1048,9 +949,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1072,9 +973,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1089,9 +990,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1106,6 +1007,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/repo-audit-analyzer"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "repo-audit-analyzer"
GH_AW_WORKFLOW_NAME: "Repository Audit & Agentic Workflow Opportunity Analyzer"
outputs:
@@ -1126,7 +1028,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1152,9 +1054,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1171,6 +1073,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: repoauditanalyzer
steps:
- name: Checkout actions folder
@@ -1183,7 +1086,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (repo-audits)
id: download_cache_repo_audits
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml
index 6882f44576b..cfe4749f1c9 100644
--- a/.github/workflows/repo-tree-map.lock.yml
+++ b/.github/workflows/repo-tree-map.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "repo-tree-map.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,15 +125,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -167,6 +167,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -183,9 +184,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -201,10 +202,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -223,11 +224,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -252,10 +253,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: repotreemap
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -277,13 +279,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -306,16 +312,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -324,152 +330,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"dev\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"dev\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -556,6 +440,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -580,8 +465,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -592,7 +477,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -600,7 +485,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -618,10 +504,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -629,10 +515,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -640,6 +531,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -657,7 +555,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -666,7 +565,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -694,7 +593,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -732,15 +631,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -750,7 +649,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -767,9 +666,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -778,18 +677,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -864,9 +763,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -889,7 +788,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -916,9 +815,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -962,6 +861,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-repo-tree-map"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -977,7 +878,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1001,9 +902,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1014,9 +915,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1038,9 +939,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1055,9 +956,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1072,6 +973,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/repo-tree-map"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "repo-tree-map"
GH_AW_WORKFLOW_NAME: "Repository Tree Map Generator"
outputs:
@@ -1092,7 +994,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1118,9 +1020,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/repository-quality-improver.lock.yml b/.github/workflows/repository-quality-improver.lock.yml
index b531c2d0950..62a70961b2a 100644
--- a/.github/workflows/repository-quality-improver.lock.yml
+++ b/.github/workflows/repository-quality-improver.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "repository-quality-improver.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,16 +125,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt_multi.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt_multi.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -168,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -188,9 +189,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -209,10 +210,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -234,11 +235,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -264,10 +265,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: repositoryqualityimprover
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -289,13 +291,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory (focus-areas)
run: |
@@ -329,16 +335,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -347,152 +353,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -579,6 +463,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -603,8 +488,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -615,7 +500,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -623,7 +508,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -641,10 +527,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -652,10 +538,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -663,6 +554,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -671,7 +569,14 @@ jobs:
"args": ["--network", "host"],
"entrypoint": "serena",
"entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -688,7 +593,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -697,7 +603,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory-focus-areas/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -725,7 +631,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -763,15 +669,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -781,7 +687,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -798,9 +704,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -809,18 +715,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -901,9 +807,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -926,7 +832,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -953,9 +859,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1000,6 +906,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-repository-quality-improver"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1015,7 +923,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1039,9 +947,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1052,9 +960,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1076,9 +984,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1093,9 +1001,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1110,6 +1018,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/repository-quality-improver"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "repository-quality-improver"
GH_AW_WORKFLOW_NAME: "Repository Quality Improvement Agent"
outputs:
@@ -1130,7 +1039,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1156,9 +1065,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1175,6 +1084,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: repositoryqualityimprover
steps:
- name: Checkout actions folder
@@ -1187,7 +1097,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (focus-areas)
id: download_cache_focus_areas
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml
index 6fbedce5c57..3592b767f5b 100644
--- a/.github/workflows/research.lock.yml
+++ b/.github/workflows/research.lock.yml
@@ -67,7 +67,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -82,18 +82,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","node"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -111,9 +111,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "research.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -129,15 +129,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -171,6 +171,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -193,9 +194,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -212,10 +213,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -235,11 +236,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -262,10 +263,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: research
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -287,13 +289,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -316,16 +322,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -334,152 +340,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"research\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"research\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -566,6 +450,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -590,8 +475,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -602,7 +487,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -610,7 +495,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }}
run: |
@@ -629,10 +515,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e TAVILY_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e TAVILY_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -640,10 +526,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -651,6 +542,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"tavily": {
@@ -664,6 +562,13 @@ jobs:
],
"env": {
"TAVILY_API_KEY": "\${TAVILY_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -681,7 +586,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -690,7 +596,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,mcp.tavily.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,mcp.tavily.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -719,7 +625,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -757,15 +663,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,TAVILY_API_KEY'
@@ -776,7 +682,7 @@ jobs:
SECRET_TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -793,9 +699,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -804,18 +710,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -890,9 +796,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -915,7 +821,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -942,9 +848,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -988,6 +894,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-research"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1003,7 +911,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1027,9 +935,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1040,9 +948,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1064,9 +972,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1081,9 +989,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1098,6 +1006,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/research"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "research"
GH_AW_WORKFLOW_NAME: "Basic Research Agent"
outputs:
@@ -1118,7 +1027,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1144,9 +1053,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/safe-output-health.lock.yml b/.github/workflows/safe-output-health.lock.yml
index 35f9f527066..667a3ff5478 100644
--- a/.github/workflows/safe-output-health.lock.yml
+++ b/.github/workflows/safe-output-health.lock.yml
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,18 +80,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -109,9 +109,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "safe-output-health.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -126,16 +126,17 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -169,6 +170,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -189,9 +191,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -210,10 +212,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -235,11 +237,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -265,10 +267,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: safeoutputhealth
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -289,7 +292,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -325,7 +328,11 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
- env:
@@ -335,7 +342,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -365,9 +372,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -375,7 +382,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -386,10 +393,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -404,158 +411,36 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -642,6 +527,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -666,8 +552,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -678,7 +564,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -686,7 +572,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -705,9 +592,9 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -719,16 +606,28 @@ jobs:
"GITHUB_TOKEN": "$GITHUB_TOKEN",
"GITHUB_ACTOR": "$GITHUB_ACTOR",
"GITHUB_REPOSITORY": "$GITHUB_REPOSITORY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -736,6 +635,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -753,7 +659,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -847,7 +754,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(/tmp/gh-aw/jqschema.sh),Bash(cat),Bash(date),Bash(echo),Bash(git),Bash(grep),Bash(head),Bash(jq *),Bash(ls),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -892,15 +799,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -910,7 +817,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -927,9 +834,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -938,18 +845,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1028,9 +935,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1063,7 +970,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1091,9 +998,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1138,6 +1045,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-safe-output-health"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1153,7 +1062,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1177,9 +1086,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1190,9 +1099,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1213,9 +1122,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1230,9 +1139,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1247,6 +1156,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/safe-output-health"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "safe-output-health"
GH_AW_WORKFLOW_NAME: "Safe Output Health Monitor"
outputs:
@@ -1267,7 +1177,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1293,9 +1203,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1312,6 +1222,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: safeoutputhealth
steps:
- name: Checkout actions folder
@@ -1324,7 +1235,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/schema-consistency-checker.lock.yml b/.github/workflows/schema-consistency-checker.lock.yml
index b5a9e11b9ee..4f346272893 100644
--- a/.github/workflows/schema-consistency-checker.lock.yml
+++ b/.github/workflows/schema-consistency-checker.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "schema-consistency-checker.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,16 +125,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -168,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -184,9 +185,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -205,10 +206,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -230,11 +231,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -260,10 +261,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: schemaconsistencychecker
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -284,16 +286,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -323,9 +329,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -333,7 +339,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -344,152 +350,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[Schema Consistency] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[Schema Consistency] \". Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -576,6 +460,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -600,8 +485,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -612,7 +497,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -620,7 +505,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -638,9 +524,9 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -648,9 +534,14 @@ jobs:
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer $GITHUB_MCP_SERVER_TOKEN",
- "X-MCP-Lockdown": "$([ "$GITHUB_MCP_LOCKDOWN" = "1" ] && echo true || echo false)",
"X-MCP-Readonly": "true",
"X-MCP-Toolsets": "context,repos,issues,pull_requests,discussions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -658,6 +549,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -675,7 +573,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -755,7 +654,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash,BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -799,15 +698,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -817,7 +716,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -834,9 +733,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -845,18 +744,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -935,9 +834,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -970,7 +869,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -998,9 +897,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1045,6 +944,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-schema-consistency-checker"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1060,7 +961,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1084,9 +985,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1097,9 +998,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1120,9 +1021,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1137,9 +1038,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1154,6 +1055,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/schema-consistency-checker"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "schema-consistency-checker"
GH_AW_WORKFLOW_NAME: "Schema Consistency Checker"
outputs:
@@ -1174,7 +1076,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1200,9 +1102,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1219,6 +1121,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: schemaconsistencychecker
steps:
- name: Checkout actions folder
@@ -1231,7 +1134,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml
index 22f3d9bb5a9..fc11fb55830 100644
--- a/.github/workflows/scout.lock.yml
+++ b/.github/workflows/scout.lock.yml
@@ -33,7 +33,7 @@
# - shared/mcp/tavily.md
# - shared/reporting.md
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"ab1cde07589013cf3883a1da93d56db5e5aa305c3baae6e98dae8954cf10dcdd","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"1257d91a7cbfe146dbfc43fb7b731d709fe84bfc4622da42ad1cf548a7f04428","strict":true}
name: "Scout"
"on":
@@ -113,8 +113,9 @@ jobs:
pull-requests: write
outputs:
body: ${{ steps.sanitized.outputs.body }}
- comment_id: ""
- comment_repo: ""
+ comment_id: ${{ steps.add-comment.outputs.comment-id }}
+ comment_repo: ${{ steps.add-comment.outputs.comment-repo }}
+ comment_url: ${{ steps.add-comment.outputs.comment-url }}
model: ${{ steps.generate_aw_info.outputs.model }}
secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
slash_command: ${{ needs.pre_activation.outputs.matched_command }}
@@ -131,7 +132,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -146,18 +147,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -178,9 +179,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -188,18 +189,31 @@ jobs:
GH_AW_WORKFLOW_FILE: "scout.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_WORKFLOW_NAME: "Scout"
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔭 *Intelligence gathered by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🏕️ Scout on patrol! [{workflow_name}]({run_url}) is blazing trails through this {event_type}...\",\"runSuccess\":\"🔭 Recon complete! [{workflow_name}]({run_url}) has charted the territory. Map ready! 🗺️\",\"runFailure\":\"🏕️ Lost in the wilderness! [{workflow_name}]({run_url}) {status}. Sending search party...\"}"
+ with:
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_workflow_run_comment.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -219,16 +233,16 @@ jobs:
GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }}
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, add_labels, missing_tool, missing_data, noop
@@ -262,8 +276,9 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
- cat "/opt/gh-aw/prompts/pr_context_prompt.md"
+ cat "${GH_AW_HOME}/prompts/pr_context_prompt.md"
fi
cat << 'GH_AW_PROMPT_EOF'
@@ -305,9 +320,9 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -333,10 +348,10 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -365,11 +380,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -392,10 +407,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: scout
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -416,19 +432,23 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup jq utilities directory
run: "mkdir -p /tmp/gh-aw\ncat > /tmp/gh-aw/jqschema.sh << 'EOF'\n#!/usr/bin/env bash\n# jqschema.sh\njq -c '\ndef walk(f):\n . as $in |\n if type == \"object\" then\n reduce keys[] as $k ({}; . + {($k): ($in[$k] | walk(f))})\n elif type == \"array\" then\n if length == 0 then [] else [.[0] | walk(f)] end\n else\n type\n end;\nwalk(.)\n'\nEOF\nchmod +x /tmp/gh-aw/jqschema.sh"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -458,9 +478,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -468,185 +488,32 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcp/arxiv-mcp-server mcp/markitdown node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcp/arxiv-mcp-server mcp/markitdown node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":1},"add_labels":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Add labels to an existing GitHub issue or pull request for categorization and filtering. Labels must already exist in the repository. For creating new issues with labels, use create_issue with the labels property instead. CONSTRAINTS: Maximum 1 label(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). If omitted, adds labels to the issue or PR that triggered this workflow. Only works for issue or pull_request event triggers. For schedule, workflow_dispatch, or other triggers, item_number is required — omitting it will silently skip the label operation.",
- "type": "number"
- },
- "labels": {
- "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "add_labels"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added.",
+ "add_labels": " CONSTRAINTS: Maximum 1 label(s) can be added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -744,6 +611,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -768,8 +636,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -780,7 +648,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -806,9 +674,9 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e TAVILY_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e TAVILY_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"arxiv": {
@@ -818,7 +686,14 @@ jobs:
"search_arxiv",
"get_paper_details",
"get_paper_pdf"
- ]
+ ],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"deepwiki": {
"type": "http",
@@ -827,7 +702,14 @@ jobs:
"read_wiki_structure",
"read_wiki_contents",
"ask_question"
- ]
+ ],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
@@ -836,6 +718,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "none",
+ "repos": "all"
+ }
}
},
"markitdown": {
@@ -843,20 +731,41 @@ jobs:
"container": "mcp/markitdown",
"tools": [
"*"
- ]
+ ],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"microsoftdocs": {
"type": "http",
"url": "https://learn.microsoft.com/api/mcp",
"tools": [
"*"
- ]
+ ],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"safeoutputs": {
"type": "http",
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"tavily": {
@@ -867,7 +776,14 @@ jobs:
},
"tools": [
"*"
- ]
+ ],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -884,7 +800,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -987,7 +904,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,learn.microsoft.com,lfs.github.com,mcp.deepwiki.com,mcp.tavily.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,learn.microsoft.com,lfs.github.com,mcp.deepwiki.com,mcp.tavily.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(/tmp/gh-aw/jqschema.sh),Bash(cat),Bash(date),Bash(echo),Bash(git),Bash(grep),Bash(head),Bash(jq *),Bash(ls),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__arxiv__get_paper_details,mcp__arxiv__get_paper_pdf,mcp__arxiv__search_arxiv,mcp__deepwiki__ask_question,mcp__deepwiki__read_wiki_contents,mcp__deepwiki__read_wiki_structure,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users,mcp__markitdown,mcp__microsoftdocs,mcp__tavily'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1031,15 +948,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,TAVILY_API_KEY'
@@ -1050,7 +967,7 @@ jobs:
SECRET_TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1068,9 +985,9 @@ jobs:
GH_AW_COMMAND: scout
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1079,18 +996,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1169,9 +1086,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1204,7 +1121,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1232,9 +1149,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1280,6 +1197,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-scout"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1295,7 +1214,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1319,9 +1238,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1332,9 +1251,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1354,9 +1273,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1371,9 +1290,28 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
+ await main();
+ - name: Update reaction comment with completion status
+ id: conclusion
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ env.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: "Scout"
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.agent.outputs.detection_conclusion }}
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔭 *Intelligence gathered by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🏕️ Scout on patrol! [{workflow_name}]({run_url}) is blazing trails through this {event_type}...\",\"runSuccess\":\"🔭 Recon complete! [{workflow_name}]({run_url}) has charted the territory. Map ready! 🗺️\",\"runFailure\":\"🏕️ Lost in the wilderness! [{workflow_name}]({run_url}) {status}. Sending search party...\"}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/notify_comment_error.cjs');
await main();
pre_activation:
@@ -1409,7 +1347,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for command workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1418,9 +1356,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check command position
id: check_command_position
@@ -1429,9 +1367,9 @@ jobs:
GH_AW_COMMANDS: "[\"scout\"]"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_command_position.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_command_position.cjs');
await main();
safe_outputs:
@@ -1447,6 +1385,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/scout"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔭 *Intelligence gathered by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🏕️ Scout on patrol! [{workflow_name}]({run_url}) is blazing trails through this {event_type}...\",\"runSuccess\":\"🔭 Recon complete! [{workflow_name}]({run_url}) has charted the territory. Map ready! 🗺️\",\"runFailure\":\"🏕️ Lost in the wilderness! [{workflow_name}]({run_url}) {status}. Sending search party...\"}"
GH_AW_WORKFLOW_ID: "scout"
GH_AW_WORKFLOW_NAME: "Scout"
@@ -1470,7 +1409,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1496,9 +1435,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1515,6 +1454,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: scout
steps:
- name: Checkout actions folder
@@ -1527,7 +1467,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/scout.md b/.github/workflows/scout.md
index d240b82251c..5e63626e253 100644
--- a/.github/workflows/scout.md
+++ b/.github/workflows/scout.md
@@ -35,7 +35,8 @@ tools:
edit:
cache-memory: true
github:
- lockdown: false
+ repos: all
+ min-integrity: none
safe-outputs:
add-comment:
max: 1
diff --git a/.github/workflows/security-alert-burndown.campaign.g.lock.yml b/.github/workflows/security-alert-burndown.campaign.g.lock.yml
index a39560dcb43..2e90b91b431 100644
--- a/.github/workflows/security-alert-burndown.campaign.g.lock.yml
+++ b/.github/workflows/security-alert-burndown.campaign.g.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "security-alert-burndown.campaign.g.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,16 +125,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt_multi.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt_multi.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, create_issue, update_project, create_project_status_update, missing_tool, missing_data, noop
@@ -168,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -181,9 +182,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -201,10 +202,10 @@ jobs:
GH_AW_MEMORY_LIST: "- **campaigns**: `/tmp/gh-aw/repo-memory/campaigns/` (branch: `memory/campaigns`)\n"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -225,11 +226,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -256,10 +257,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: securityalertburndown.campaign.g
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -280,13 +282,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Create workspace directory
run: mkdir -p ./.gh-aw
- env:
@@ -314,7 +320,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/campaigns
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -337,9 +343,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -347,7 +353,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -358,415 +364,33 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":3},"create_issue":{"max":1},"create_project_status_update":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/campaigns","id":"campaigns","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]},"update_project":{"max":10}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 3 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Manage GitHub Projects: add issues/pull requests/draft issues, update item fields (status, priority, effort, dates), manage custom fields, and create project views. Use this to organize work by adding items to projects, updating field values, creating custom fields up-front, and setting up project views (table, board, roadmap).\n\nThree modes: (1) Add or update project items with custom field values; (2) Create project fields; (3) Create project views. This is the primary tool for ProjectOps automation - add items to projects, set custom fields for tracking, and organize project boards. CONSTRAINTS: Maximum 10 project operation(s) can be performed. Default project URL: \"https://github.com/orgs/githubnext/projects/122\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "content_number": {
- "description": "Issue or pull request number to add to the project. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123 for issue #123, or 456 in github.com/owner/repo/pull/456 for PR #456), or a temporary ID from a recent create_issue call (e.g., 'aw_abc123', '#aw_Test123'). Required when content_type is 'issue' or 'pull_request'.",
- "type": [
- "number",
- "string"
- ]
- },
- "content_type": {
- "description": "Type of item to add to the project. Use 'issue' or 'pull_request' to add existing repo content, or 'draft_issue' to create a draft item inside the project. Required when operation is not specified.",
- "enum": [
- "issue",
- "pull_request",
- "draft_issue"
- ],
- "type": "string"
- },
- "create_if_missing": {
- "description": "Whether to create the project if it doesn't exist. Defaults to false. Requires projects:write permission when true.",
- "type": "boolean"
- },
- "draft_body": {
- "description": "Optional body for a Projects v2 draft issue (markdown). Only used when content_type is 'draft_issue'.",
- "type": "string"
- },
- "draft_issue_id": {
- "description": "Temporary ID of an existing draft issue to update (e.g., 'aw_abc1', '#aw_Test123'). Use this to reference a draft created earlier with a matching temporary_id. When provided, draft_title is not required for updates.",
- "pattern": "^#?aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "draft_title": {
- "description": "Title for a Projects v2 draft issue. Required when content_type is 'draft_issue'.",
- "type": "string"
- },
- "field_definitions": {
- "description": "Field definitions to create when operation is create_fields. Required when operation='create_fields'.",
- "items": {
- "additionalProperties": false,
- "properties": {
- "data_type": {
- "description": "Field type. Use SINGLE_SELECT with options for enumerated values.",
- "enum": [
- "TEXT",
- "NUMBER",
- "DATE",
- "SINGLE_SELECT",
- "ITERATION"
- ],
- "type": "string"
- },
- "name": {
- "description": "Field name to create (e.g., 'size', 'priority').",
- "type": "string"
- },
- "options": {
- "description": "Options for SINGLE_SELECT fields.",
- "items": {
- "type": "string"
- },
- "type": "array"
- }
- },
- "required": [
- "name",
- "data_type"
- ],
- "type": "object"
- },
- "type": "array"
- },
- "fields": {
- "description": "Custom field values to set on the project item (e.g., {'Status': 'In Progress', 'Priority': 'High'}). Field names must match custom fields defined in the project.",
- "type": "object"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "operation": {
- "description": "Optional operation mode. Use create_fields to create required fields up-front, or create_view to add a project view. When omitted, the tool adds/updates project items.",
- "enum": [
- "create_fields",
- "create_view"
- ],
- "type": "string"
- },
- "project": {
- "description": "Full GitHub project URL (e.g., 'https://github.com/orgs/myorg/projects/42' or 'https://github.com/users/username/projects/5'), or a temporary project ID from a recent create_project call (e.g., '#aw_abc1', 'aw_Test123'). Project names or numbers alone are NOT accepted.",
- "pattern": "^(https://github\\.com/(orgs|users)/[^/]+/projects/\\d+|#?aw_[A-Za-z0-9]{3,12})$",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this draft issue (e.g., 'aw_abc1', '#aw_Test123'). Provide this when creating a new draft to enable future updates via draft_issue_id. Format: optional leading '#', then 'aw_' followed by 3 to 12 alphanumeric characters.",
- "pattern": "^#?aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "view": {
- "additionalProperties": false,
- "description": "View definition to create when operation is create_view. Required when operation='create_view'.",
- "properties": {
- "filter": {
- "type": "string"
- },
- "layout": {
- "enum": [
- "table",
- "board",
- "roadmap"
- ],
- "type": "string"
- },
- "name": {
- "type": "string"
- },
- "visible_fields": {
- "description": "Field IDs to show in the view (table/board only).",
- "items": {
- "type": "number"
- },
- "type": "array"
- }
- },
- "required": [
- "name",
- "layout"
- ],
- "type": "object"
- }
- },
- "required": [
- "project"
- ],
- "type": "object"
- },
- "name": "update_project"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- },
- {
- "description": "Post a status update to a GitHub Project to communicate progress and health. Use this to provide stakeholders with regular updates on project status (on-track, at-risk, off-track, complete, inactive), timeline information, and progress summaries. Status updates create a historical record of project progress, enabling tracking over time and informed decision-making. CONSTRAINTS: Maximum 1 status update(s) can be created. Default project URL: \"https://github.com/orgs/githubnext/projects/122\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Status update body in markdown format describing progress, findings, trends, and next steps. Should provide stakeholders with clear understanding of project state.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "project": {
- "description": "Full GitHub project URL (e.g., 'https://github.com/orgs/myorg/projects/42' or 'https://github.com/users/username/projects/5'). Project names or numbers alone are NOT accepted.",
- "pattern": "^https://github\\\\.com/(orgs|users)/[^/]+/projects/\\\\d+$",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "start_date": {
- "description": "Optional project start date in YYYY-MM-DD format (e.g., '2026-01-06').",
- "pattern": "^\\\\d{4}-\\\\d{2}-\\\\d{2}$",
- "type": "string"
- },
- "status": {
- "description": "Status indicator for the project. Defaults to ON_TRACK. Values: ON_TRACK (progressing well), AT_RISK (has issues/blockers), OFF_TRACK (significantly behind), COMPLETE (finished), INACTIVE (paused/cancelled).",
- "enum": [
- "ON_TRACK",
- "AT_RISK",
- "OFF_TRACK",
- "COMPLETE",
- "INACTIVE"
- ],
- "type": "string"
- },
- "target_date": {
- "description": "Optional project target/end date in YYYY-MM-DD format (e.g., '2026-12-31').",
- "pattern": "^\\\\d{4}-\\\\d{2}-\\\\d{2}$",
- "type": "string"
- }
- },
- "required": [
- "project",
- "body"
- ],
- "type": "object"
- },
- "name": "create_project_status_update"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 3 comment(s) can be added.",
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created.",
+ "create_project_status_update": " CONSTRAINTS: Maximum 1 status update(s) can be created. Default project URL: \"https://github.com/orgs/githubnext/projects/122\".",
+ "update_project": " CONSTRAINTS: Maximum 10 project operation(s) can be performed. Default project URL: \"https://github.com/orgs/githubnext/projects/122\"."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -960,6 +584,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -984,8 +609,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -996,7 +621,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -1004,7 +629,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -1022,19 +648,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,actions,code_security"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -1042,6 +673,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -1059,7 +697,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -1135,7 +774,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools Bash,BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1179,15 +818,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -1197,7 +836,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1214,9 +853,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1225,18 +864,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1318,9 +957,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1353,7 +992,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1381,9 +1020,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1429,6 +1068,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-security-alert-burndown.campaign.g"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1444,7 +1085,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1468,9 +1109,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1481,9 +1122,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1506,9 +1147,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1523,9 +1164,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1537,6 +1178,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
patch_size_exceeded_campaigns: ${{ steps.push_repo_memory_campaigns.outputs.patch_size_exceeded }}
validation_error_campaigns: ${{ steps.push_repo_memory_campaigns.outputs.validation_error }}
@@ -1552,7 +1195,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1595,9 +1238,9 @@ jobs:
FILE_GLOB_FILTER: "security-alert-burndown/**"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1613,6 +1256,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/security-alert-burndown.campaign.g"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "security-alert-burndown.campaign.g"
GH_AW_WORKFLOW_NAME: "Security Alert Burndown"
outputs:
@@ -1637,7 +1281,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
safe-output-custom-tokens: 'true'
- name: Download agent output artifact
id: download-agent-output
@@ -1666,9 +1310,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/security-compliance.lock.yml b/.github/workflows/security-compliance.lock.yml
index ada91389520..61ee5a21946 100644
--- a/.github/workflows/security-compliance.lock.yml
+++ b/.github/workflows/security-compliance.lock.yml
@@ -72,7 +72,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -87,18 +87,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -116,18 +116,18 @@ jobs:
GH_AW_WORKFLOW_FILE: "security-compliance.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/compute_text.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -146,16 +146,16 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -189,6 +189,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -206,9 +207,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -233,10 +234,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -264,11 +265,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -290,10 +291,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: securitycompliance
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -315,13 +317,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Repo memory git-based storage configuration from frontmatter processed below
- name: Clone repo-memory branch (default)
env:
@@ -331,7 +337,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -354,16 +360,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -372,182 +378,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":48,"group":true,"max":100},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 100 issue(s) can be created. Labels [\"security\" \"campaign-tracker\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 100 issue(s) can be created. Labels [\"security\" \"campaign-tracker\" \"cookie\"] will be automatically added."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -641,6 +495,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -665,8 +520,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -677,7 +532,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -685,7 +540,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -703,10 +559,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -714,10 +570,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "repos,search,code_security"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -725,6 +586,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -742,7 +610,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -751,7 +620,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -779,7 +648,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -817,15 +686,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -835,7 +704,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -852,9 +721,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -863,18 +732,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -958,9 +827,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -983,7 +852,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1010,9 +879,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1056,6 +925,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-security-compliance"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1071,7 +942,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1095,9 +966,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1108,9 +979,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1134,9 +1005,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1151,9 +1022,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
push_repo_memory:
@@ -1165,6 +1036,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1180,7 +1053,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1223,9 +1096,9 @@ jobs:
FILE_GLOB_FILTER: "memory/campaigns/security-compliance-*/**"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1239,6 +1112,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/security-compliance"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "security-compliance"
GH_AW_WORKFLOW_NAME: "Security Compliance Campaign"
outputs:
@@ -1261,7 +1135,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1287,9 +1161,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/security-review.lock.yml b/.github/workflows/security-review.lock.yml
index 90b9598183b..fc8b6cfd39a 100644
--- a/.github/workflows/security-review.lock.yml
+++ b/.github/workflows/security-review.lock.yml
@@ -58,8 +58,9 @@ jobs:
pull-requests: write
outputs:
body: ${{ steps.sanitized.outputs.body }}
- comment_id: ""
- comment_repo: ""
+ comment_id: ${{ steps.add-comment.outputs.comment-id }}
+ comment_repo: ${{ steps.add-comment.outputs.comment-repo }}
+ comment_url: ${{ steps.add-comment.outputs.comment-url }}
model: ${{ steps.generate_aw_info.outputs.model }}
secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
slash_command: ${{ needs.pre_activation.outputs.matched_command }}
@@ -76,7 +77,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -91,18 +92,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -123,9 +124,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -133,18 +134,31 @@ jobs:
GH_AW_WORKFLOW_FILE: "security-review.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_WORKFLOW_NAME: "Security Review Agent 🔒"
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔒 *Security review by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔍 [{workflow_name}]({run_url}) is analyzing this {event_type} for security implications...\",\"runSuccess\":\"🔒 [{workflow_name}]({run_url}) completed the security review.\",\"runFailure\":\"⚠️ [{workflow_name}]({run_url}) {status} during security review.\"}"
+ with:
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_workflow_run_comment.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -161,16 +175,17 @@ jobs:
GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }}
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request_review_comment, submit_pull_request_review, missing_tool, missing_data, noop
@@ -204,8 +219,9 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
- cat "/opt/gh-aw/prompts/pr_context_prompt.md"
+ cat "${GH_AW_HOME}/prompts/pr_context_prompt.md"
fi
cat << 'GH_AW_PROMPT_EOF'
@@ -223,9 +239,9 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -248,10 +264,10 @@ jobs:
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -277,11 +293,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -307,10 +323,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: securityreview
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -332,7 +349,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -368,10 +385,14 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -401,16 +422,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -419,10 +440,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -437,219 +458,37 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request_review_comment":{"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1},"submit_pull_request_review":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a review comment on a specific line of code in a pull request. Use this for inline code review feedback, suggestions, or questions about specific code changes. For general PR comments not tied to specific lines, use add_comment instead. When the workflow is configured with `target: \"*\"`, you must specify `pull_request_number` to indicate which PR to target. CONSTRAINTS: Maximum 10 review comment(s) can be created. Comments will be on the RIGHT side of the diff.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Review comment content in Markdown. Provide specific, actionable feedback about the code at this location.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "line": {
- "description": "Line number for the comment. For single-line comments, this is the target line. For multi-line comments, this is the ending line.",
- "type": [
- "number",
- "string"
- ]
- },
- "path": {
- "description": "File path relative to the repository root (e.g., 'src/auth/login.js'). Must be a file that was changed in the PR.",
- "type": "string"
- },
- "pull_request_number": {
- "description": "Pull request number to add the review comment to. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, adds the comment to the PR that triggered this workflow. Required when the workflow target is '*' (any PR) — omitting it will cause the comment to fail.",
- "type": [
- "number",
- "string"
- ]
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. If omitted, uses the configured target repository. Must be in the allowed-repos list if specified.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "side": {
- "description": "Side of the diff to comment on: RIGHT for the new version (additions), LEFT for the old version (deletions). Defaults to RIGHT.",
- "enum": [
- "LEFT",
- "RIGHT"
- ],
- "type": "string"
- },
- "start_line": {
- "description": "Starting line number for multi-line comments. When set, the comment spans from start_line to line. Omit for single-line comments.",
- "type": [
- "number",
- "string"
- ]
- }
- },
- "required": [
- "path",
- "line",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request_review_comment"
- },
- {
- "description": "Submit a pull request review with a status decision. All create_pull_request_review_comment outputs are automatically collected and included as inline comments in this review. Use APPROVE to approve the PR, REQUEST_CHANGES to request changes, or COMMENT for general feedback without a decision. If you don't call this tool, review comments are still submitted as a COMMENT review. CONSTRAINTS: Maximum 1 review(s) can be submitted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Overall review summary in Markdown. Provide a high-level assessment of the changes. Required for REQUEST_CHANGES; optional for APPROVE and COMMENT.",
- "type": "string"
- },
- "event": {
- "description": "Review decision: APPROVE to approve the pull request, REQUEST_CHANGES to formally request changes before merging, or COMMENT for general feedback without a formal decision. Defaults to COMMENT when omitted.",
- "enum": [
- "APPROVE",
- "REQUEST_CHANGES",
- "COMMENT"
- ],
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "submit_pull_request_review"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request_review_comment": " CONSTRAINTS: Maximum 10 review comment(s) can be created. Comments will be on the RIGHT side of the diff.",
+ "submit_pull_request_review": " CONSTRAINTS: Maximum 1 review(s) can be submitted."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request_review_comment": {
"defaultMax": 1,
@@ -765,6 +604,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -789,8 +629,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -801,7 +641,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -809,7 +649,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -828,10 +669,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -844,6 +685,13 @@ jobs:
"GITHUB_TOKEN": "\${GITHUB_TOKEN}",
"GITHUB_ACTOR": "\${GITHUB_ACTOR}",
"GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
@@ -851,10 +699,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "all"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -862,6 +715,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -879,7 +739,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -888,7 +749,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -916,7 +777,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -954,15 +815,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -972,7 +833,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -990,9 +851,9 @@ jobs:
GH_AW_COMMAND: security-review
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1001,18 +862,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1093,9 +954,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1118,7 +979,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1145,9 +1006,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1191,6 +1052,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-security-review"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1206,7 +1069,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1230,9 +1093,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1243,9 +1106,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1266,9 +1129,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1283,9 +1146,28 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
+ await main();
+ - name: Update reaction comment with completion status
+ id: conclusion
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ env.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: "Security Review Agent 🔒"
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.agent.outputs.detection_conclusion }}
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔒 *Security review by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔍 [{workflow_name}]({run_url}) is analyzing this {event_type} for security implications...\",\"runSuccess\":\"🔒 [{workflow_name}]({run_url}) completed the security review.\",\"runFailure\":\"⚠️ [{workflow_name}]({run_url}) {status} during security review.\"}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/notify_comment_error.cjs');
await main();
pre_activation:
@@ -1310,7 +1192,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for command workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1319,9 +1201,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check command position
id: check_command_position
@@ -1330,9 +1212,9 @@ jobs:
GH_AW_COMMANDS: "[\"security-review\"]"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_command_position.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_command_position.cjs');
await main();
safe_outputs:
@@ -1346,6 +1228,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/security-review"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔒 *Security review by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔍 [{workflow_name}]({run_url}) is analyzing this {event_type} for security implications...\",\"runSuccess\":\"🔒 [{workflow_name}]({run_url}) completed the security review.\",\"runFailure\":\"⚠️ [{workflow_name}]({run_url}) {status} during security review.\"}"
GH_AW_WORKFLOW_ID: "security-review"
GH_AW_WORKFLOW_NAME: "Security Review Agent 🔒"
@@ -1367,7 +1250,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1393,9 +1276,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1412,6 +1295,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: securityreview
steps:
- name: Checkout actions folder
@@ -1424,7 +1308,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml
index eefe13cf0a6..bec779520a6 100644
--- a/.github/workflows/semantic-function-refactor.lock.yml
+++ b/.github/workflows/semantic-function-refactor.lock.yml
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,18 +80,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -109,9 +109,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "semantic-function-refactor.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -126,15 +126,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, close_issue, missing_tool, missing_data, noop
@@ -168,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -189,9 +190,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -207,10 +208,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -229,11 +230,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -258,10 +259,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: semanticfunctionrefactor
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -282,13 +284,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -311,9 +317,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -321,7 +327,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -332,199 +338,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"close_issue":{"max":10,"required_title_prefix":"[refactor] ","target":"*"},"create_issue":{"expires":48,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[refactor] \". Labels [\"refactoring\" \"code-quality\" \"automated-analysis\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Close a GitHub issue with a closing comment. You can and should always add a comment when closing an issue to explain the action or provide context. This tool is ONLY for closing issues - use update_issue if you need to change the title, body, labels, or other metadata without closing. Use close_issue when work is complete, the issue is no longer relevant, or it's a duplicate. The closing comment should explain the resolution or reason for closing. If the issue is already closed, a comment will still be posted. CONSTRAINTS: Maximum 10 issue(s) can be closed. Target: *. Only issues with title prefix \"[refactor] \" can be closed.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Closing comment explaining why the issue is being closed and summarizing any resolution, workaround, or conclusion.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "issue_number": {
- "description": "Issue number to close. This is the numeric ID from the GitHub URL (e.g., 901 in github.com/owner/repo/issues/901). If omitted, closes the issue that triggered this workflow (requires an issue event trigger).",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "close_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "close_issue": " CONSTRAINTS: Maximum 10 issue(s) can be closed. Target: *. Only issues with title prefix \"[refactor] \" can be closed.",
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[refactor] \". Labels [\"refactoring\" \"code-quality\" \"automated-analysis\" \"cookie\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"close_issue": {
"defaultMax": 1,
@@ -636,6 +474,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -660,8 +499,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -672,7 +511,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -680,7 +519,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -698,19 +538,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -718,6 +563,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -734,7 +586,14 @@ jobs:
"--project",
"\${GITHUB_WORKSPACE}"
],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -751,7 +610,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -846,7 +706,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(cat pkg/**/*.go),Bash(cat),Bash(date),Bash(echo),Bash(find pkg -name '\''\'\'''\''*.go'\''\'\'''\'' ! -name '\''\'\'''\''*_test.go'\''\'\'''\'' -type f),Bash(find pkg -type f -name '\''\'\'''\''*.go'\''\'\'''\'' ! -name '\''\'\'''\''*_test.go'\''\'\'''\''),Bash(find pkg/ -maxdepth 1 -ls),Bash(find pkg/workflow/ -maxdepth 1 -ls),Bash(grep -r '\''\'\'''\''func '\''\'\'''\'' pkg --include='\''\'\'''\''*.go'\''\'\'''\''),Bash(grep),Bash(head -n * pkg/**/*.go),Bash(head),Bash(ls),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc -l pkg/**/*.go),Bash(wc),Bash(yq),BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -890,15 +750,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -908,7 +768,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -925,9 +785,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -936,18 +796,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1020,9 +880,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1055,7 +915,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1083,9 +943,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1128,6 +988,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-semantic-function-refactor"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1143,7 +1005,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1167,9 +1029,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1180,9 +1042,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1201,9 +1063,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1218,9 +1080,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1234,6 +1096,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/semantic-function-refactor"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "semantic-function-refactor"
GH_AW_WORKFLOW_NAME: "Semantic Function Refactoring"
outputs:
@@ -1256,7 +1119,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1282,9 +1145,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/sergo.lock.yml b/.github/workflows/sergo.lock.yml
index 24868a8e7ca..5082479ccce 100644
--- a/.github/workflows/sergo.lock.yml
+++ b/.github/workflows/sergo.lock.yml
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,18 +80,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github","go"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate ANTHROPIC_API_KEY secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Checkout .github and .agents folders
@@ -109,9 +109,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "sergo.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -126,16 +126,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, missing_tool, missing_data, noop
@@ -169,6 +169,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -190,9 +191,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -211,10 +212,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -236,11 +237,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -266,10 +267,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: sergo
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -290,16 +292,20 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -329,9 +335,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
@@ -339,7 +345,7 @@ jobs:
node-version: '24'
package-manager-cache: false
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Install Claude Code CLI
run: npm install -g @anthropic-ai/claude-code@latest
- name: Determine automatic lockdown mode for GitHub MCP Server
@@ -350,152 +356,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[sergo] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[sergo] \". Discussions will be created in category \"audits\"."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -582,6 +466,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -606,8 +491,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -618,7 +503,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -626,7 +511,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -644,19 +530,24 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="claude"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "$GITHUB_SERVER_URL",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_SERVER_TOKEN",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -664,6 +555,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"serena": {
@@ -680,7 +578,14 @@ jobs:
"--project",
"\${GITHUB_WORKSPACE}"
],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -697,7 +602,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute Claude Code CLI
id: agentic_execution
# Allowed tools (sorted):
@@ -794,7 +700,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(cat go.mod),Bash(cat go.sum),Bash(cat),Bash(date),Bash(echo),Bash(find . -name '\''\'\'''\''*.go'\''\'\'''\'' -type f),Bash(go list -m all),Bash(grep -r '\''\'\'''\''func '\''\'\'''\'' --include='\''\'\'''\''*.go'\''\'\'''\''),Bash(grep),Bash(head),Bash(ls),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc -l),Bash(wc),Bash(yq),BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -838,15 +744,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'ANTHROPIC_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -856,7 +762,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -868,14 +774,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -884,18 +790,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_claude_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_claude_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -974,9 +880,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1009,7 +915,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --allowed-tools '\''Bash(cat),Bash(grep),Bash(head),Bash(jq),Bash(ls),Bash(tail),Bash(wc),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite'\'' --debug-file /tmp/gh-aw/threat-detection/detection.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1037,9 +943,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1084,6 +990,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-sergo"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1099,7 +1007,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1124,9 +1032,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1138,9 +1046,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1162,9 +1070,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1180,9 +1088,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1197,6 +1105,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/sergo"
GH_AW_ENGINE_ID: "claude"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "sergo-daily"
GH_AW_WORKFLOW_ID: "sergo"
GH_AW_WORKFLOW_NAME: "Sergo - Serena Go Expert"
@@ -1218,7 +1127,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1237,16 +1146,16 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,pypi.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,statsig.anthropic.com,storage.googleapis.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[sergo] \"},\"missing_data\":{},\"missing_tool\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1263,6 +1172,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: sergo
steps:
- name: Checkout actions folder
@@ -1275,7 +1185,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
diff --git a/.github/workflows/shared/activation-app.md b/.github/workflows/shared/activation-app.md
index 7f3d19321a5..9bdca986285 100644
--- a/.github/workflows/shared/activation-app.md
+++ b/.github/workflows/shared/activation-app.md
@@ -1,8 +1,8 @@
---
-#on:
-# github-app:
-# app-id: ${{ vars.APP_ID }}
-# private-key: ${{ secrets.APP_PRIVATE_KEY }}
+on:
+ github-app:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
---
diff --git a/.github/workflows/shared/python-nlp.md b/.github/workflows/shared/python-nlp.md
new file mode 100644
index 00000000000..cd62d6de032
--- /dev/null
+++ b/.github/workflows/shared/python-nlp.md
@@ -0,0 +1,104 @@
+---
+# Python NLP Environment - scikit-learn, NLTK, TextBlob, WordCloud
+# Provides TF-IDF vectorization, K-means clustering, sentiment analysis, and NLP utilities
+
+tools:
+ bash:
+ - "*"
+
+network:
+ allowed:
+ - python
+
+steps:
+ - name: Setup Python NLP environment
+ run: |
+ mkdir -p /tmp/gh-aw/python/{data,charts,artifacts}
+ pip install --user --quiet nltk scikit-learn textblob wordcloud
+
+ # Download required NLTK corpora
+ python3 -c "
+ import nltk
+ for corpus in ['punkt_tab', 'stopwords', 'vader_lexicon', 'averaged_perceptron_tagger_eng']:
+ nltk.download(corpus, quiet=True)
+ print('NLTK corpora ready')
+ "
+
+ python3 -c "import sklearn; print(f'scikit-learn {sklearn.__version__}')"
+---
+
+## Python NLP Environment Ready
+
+Libraries: scikit-learn, NLTK, TextBlob, WordCloud
+Directories: `/tmp/gh-aw/python/{data,charts,artifacts}`
+
+### TF-IDF + K-means Clustering Pattern
+
+```python
+from sklearn.feature_extraction.text import TfidfVectorizer
+from sklearn.cluster import KMeans
+import numpy as np
+
+# Vectorize text
+vectorizer = TfidfVectorizer(max_features=500, stop_words='english', ngram_range=(1, 2))
+X = vectorizer.fit_transform(texts)
+
+# Find optimal k using elbow method
+inertias = []
+k_range = range(2, min(11, len(texts)))
+for k in k_range:
+ km = KMeans(n_clusters=k, random_state=42, n_init=10)
+ km.fit(X)
+ inertias.append(km.inertia_)
+
+# Fit final model
+optimal_k = 5 # or determine from elbow
+kmeans = KMeans(n_clusters=optimal_k, random_state=42, n_init=10)
+labels = kmeans.fit_predict(X)
+
+# Label clusters by top TF-IDF terms
+feature_names = vectorizer.get_feature_names_out()
+for cluster_id in range(optimal_k):
+ center = kmeans.cluster_centers_[cluster_id]
+ top_terms = [feature_names[i] for i in center.argsort()[-10:][::-1]]
+ print(f"Cluster {cluster_id}: {', '.join(top_terms)}")
+```
+
+### Sentiment Analysis Pattern (TextBlob / VADER)
+
+```python
+from textblob import TextBlob
+from nltk.sentiment.vader import SentimentIntensityAnalyzer
+
+# TextBlob polarity (-1 negative, +1 positive)
+blob = TextBlob(text)
+polarity = blob.sentiment.polarity
+
+# VADER compound score (-1 to +1)
+sia = SentimentIntensityAnalyzer()
+scores = sia.polarity_scores(text)
+compound = scores['compound'] # >= 0.05 positive, <= -0.05 negative
+```
+
+### Text Preprocessing
+
+```python
+import re
+from nltk.corpus import stopwords
+
+stop_words = set(stopwords.words('english'))
+
+def clean_text(text):
+ text = re.sub(r'```.*?```', '', text, flags=re.DOTALL) # Remove code blocks
+ text = re.sub(r'http\S+', '', text) # Remove URLs
+ text = re.sub(r'[^a-zA-Z\s]', ' ', text) # Keep only letters
+ tokens = text.lower().split()
+ return ' '.join(t for t in tokens if t not in stop_words and len(t) > 2)
+```
+
+### Best Practices
+
+- Use JSON Lines (`.jsonl`) for append-only historical storage
+- Cache vectorizers to avoid re-fitting on the same data
+- Label clusters by top TF-IDF terms
+- Use VADER for short social text; TextBlob for longer prose
diff --git a/.github/workflows/shared/safe-output-app.md b/.github/workflows/shared/safe-output-app.md
index 1ee0521c0d0..134c4d47bc6 100644
--- a/.github/workflows/shared/safe-output-app.md
+++ b/.github/workflows/shared/safe-output-app.md
@@ -1,8 +1,8 @@
---
-#safe-outputs:
-# app:
-# app-id: ${{ vars.APP_ID }}
-# private-key: ${{ secrets.APP_PRIVATE_KEY }}
+safe-outputs:
+ github-app:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
---
+- `package-name`: X.Y.Z → A.B.C
+
+### Migration Notes
+
+
+
+### Build Status
+
+✅ `npm run build` passes with updated dependencies
+
+---
+*Automated by [Update Astro](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})*
+```
+
+## Guidelines
+
+- **Only update packages in `docs/`** — do not touch root-level or `actions/` packages
+- **Always verify the build passes** before creating a PR
+- **Apply migration fixes proactively** — don't just update versions and hope for the best
+- **Focus on Astro and Starlight** for migration guidance, as these are the core frameworks
+- **Be conservative with `package-lock.json`** — run `npm install` (not `npm ci`) so the lock file is regenerated from the updated `package.json`
+
+## Important
+
+If no action is needed after completing your work (e.g., build fails and cannot be fixed), you **MUST** call the `noop` safe-output tool with a brief explanation.
+
+```json
+{"noop": {"message": "No action needed: [brief explanation]"}}
+```
diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml
index aa9fb4e4814..1a8679d7463 100644
--- a/.github/workflows/video-analyzer.lock.yml
+++ b/.github/workflows/video-analyzer.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,18 +81,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -110,9 +110,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "video-analyzer.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -128,15 +128,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -170,6 +170,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -189,9 +190,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -208,10 +209,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -231,11 +232,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -258,10 +259,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: videoanalyzer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -283,13 +285,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- id: setup-ffmpeg
name: Setup FFmpeg
run: |-
@@ -320,16 +326,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -338,167 +344,30 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":48,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[video-analysis] \". Labels [\"automation\" \"video-processing\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[video-analysis] \". Labels [\"automation\" \"video-processing\" \"cookie\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -592,6 +461,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -616,8 +486,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -628,7 +498,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -636,7 +506,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -654,10 +525,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -665,10 +536,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -676,6 +552,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -693,7 +576,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -719,7 +603,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(ffmpeg *)'\'' --allow-tool '\''shell(ffprobe *)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -747,7 +631,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -785,15 +669,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -803,7 +687,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -820,9 +704,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -831,18 +715,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -917,9 +801,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -942,7 +826,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -969,9 +853,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1014,6 +898,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-video-analyzer"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1029,7 +915,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1053,9 +939,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1066,9 +952,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1088,9 +974,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1105,9 +991,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1121,6 +1007,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/video-analyzer"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "video-analyzer"
GH_AW_WORKFLOW_NAME: "Video Analysis Agent"
outputs:
@@ -1143,7 +1030,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1169,9 +1056,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/weekly-editors-health-check.lock.yml b/.github/workflows/weekly-editors-health-check.lock.yml
index d6d9c617e88..7111bf5f447 100644
--- a/.github/workflows/weekly-editors-health-check.lock.yml
+++ b/.github/workflows/weekly-editors-health-check.lock.yml
@@ -60,7 +60,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -75,18 +75,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","playwright","github.github.com","ashleywolf.github.io","mossaka.github.io"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -104,9 +104,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "weekly-editors-health-check.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -121,21 +121,21 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/playwright_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/playwright_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, upload_asset, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
upload_asset: provide a file path; returns a URL; assets are published after the workflow completes (safeoutputs).
@@ -169,6 +169,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -184,9 +185,9 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -202,10 +203,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -224,11 +225,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -252,10 +253,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: weeklyeditorshealthcheck
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -277,13 +279,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -306,16 +312,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -324,192 +330,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcr.microsoft.com/playwright/mcp node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 mcr.microsoft.com/playwright/mcp node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request":{"expires":168,"max":1,"reviewers":["copilot"],"title_prefix":"[docs] "},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":5}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[docs] \". Labels [\"documentation\" \"automation\"] will be automatically added. Reviewers [\"copilot\"] will be assigned.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum 5 asset(s) can be uploaded. Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[docs] \". Labels [\"documentation\" \"automation\"] will be automatically added. Reviewers [\"copilot\"] will be assigned.",
+ "upload_asset": " CONSTRAINTS: Maximum 5 asset(s) can be uploaded. Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -615,6 +460,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -639,8 +485,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -651,7 +497,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -662,7 +508,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -681,10 +528,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -692,10 +539,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"playwright": {
@@ -703,13 +555,27 @@ jobs:
"container": "mcr.microsoft.com/playwright/mcp",
"args": ["--init", "--network", "host", "--security-opt", "seccomp=unconfined", "--ipc=host"],
"entrypointArgs": ["--output-dir", "/tmp/gh-aw/mcp-logs/playwright", "--no-sandbox"],
- "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"]
+ "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"safeoutputs": {
"type": "http",
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -727,7 +593,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -762,7 +629,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,ashleywolf.github.io,azure.archive.ubuntu.com,cdn.playwright.dev,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,github.github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,mossaka.github.io,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,ashleywolf.github.io,azure.archive.ubuntu.com,cdn.playwright.dev,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,github.github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,mossaka.github.io,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(cat*)'\'' --allow-tool '\''shell(curl*)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(git add:*)'\'' --allow-tool '\''shell(git branch:*)'\'' --allow-tool '\''shell(git checkout:*)'\'' --allow-tool '\''shell(git commit:*)'\'' --allow-tool '\''shell(git merge:*)'\'' --allow-tool '\''shell(git rm:*)'\'' --allow-tool '\''shell(git status)'\'' --allow-tool '\''shell(git switch:*)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool web_fetch --allow-tool write --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -793,7 +660,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -831,15 +698,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -849,7 +716,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -866,9 +733,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -877,18 +744,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -973,9 +840,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -998,7 +865,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1025,9 +892,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1072,6 +939,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-weekly-editors-health-check"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1087,7 +956,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1112,9 +981,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1126,9 +995,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1151,9 +1020,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1169,9 +1038,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1184,9 +1053,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
safe_outputs:
@@ -1203,6 +1072,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/weekly-editors-health-check"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "weekly-editors-health-check"
GH_AW_WORKFLOW_ID: "weekly-editors-health-check"
GH_AW_WORKFLOW_NAME: "Weekly Editors Health Check"
@@ -1226,7 +1096,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1281,9 +1151,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1314,7 +1184,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1370,8 +1240,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/weekly-editors-health-check.md b/.github/workflows/weekly-editors-health-check.md
index adf08486479..3e5c6c44a5f 100644
--- a/.github/workflows/weekly-editors-health-check.md
+++ b/.github/workflows/weekly-editors-health-check.md
@@ -115,13 +115,18 @@ After updating the documentation file, use the `create-pull-request` safe output
Example body (rows reflect whatever editors were discovered in Step 0):
```markdown
-## Editor Health Report –
+### Editor Health Report –
+
+
+Editor Status & Screenshots
| Editor | URL | Status | Preview |
|--------|-----|--------|---------|
| Compiler Playground | https://github.github.com/gh-aw/editor/ | ✅ 200 | ![preview]() |
| Agentic Prompt Generator | https://ashleywolf.github.io/agentic-prompt-generator/ | ✅ 200 | ![preview]() |
| ... | ... | ... | ... |
+
+
```
## Error Handling
diff --git a/.github/workflows/weekly-issue-summary.lock.yml b/.github/workflows/weekly-issue-summary.lock.yml
index e08038b5159..302f9b88cbb 100644
--- a/.github/workflows/weekly-issue-summary.lock.yml
+++ b/.github/workflows/weekly-issue-summary.lock.yml
@@ -65,7 +65,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -80,7 +80,7 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","node","python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
@@ -90,11 +90,11 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -112,9 +112,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "weekly-issue-summary.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -129,16 +129,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/cache_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_discussion, upload_asset, missing_tool, missing_data, noop
@@ -174,6 +174,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -197,9 +198,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -218,10 +219,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -243,11 +244,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -271,10 +272,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg"
GH_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}"
GH_AW_ASSETS_MAX_SIZE_KB: 10240
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: weeklyissuesummary
outputs:
detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
@@ -295,13 +297,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Setup Python environment
run: "# Create working directory for Python scripts\nmkdir -p /tmp/gh-aw/python\nmkdir -p /tmp/gh-aw/python/data\nmkdir -p /tmp/gh-aw/python/charts\nmkdir -p /tmp/gh-aw/python/artifacts\n\necho \"Python environment setup complete\"\necho \"Working directory: /tmp/gh-aw/python\"\necho \"Data directory: /tmp/gh-aw/python/data\"\necho \"Charts directory: /tmp/gh-aw/python/charts\"\necho \"Artifacts directory: /tmp/gh-aw/python/artifacts\"\n"
- name: Install Python scientific libraries
@@ -327,7 +333,7 @@ jobs:
# Cache memory file share configuration from frontmatter processed below
- name: Create cache-memory directory
- run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
+ run: bash ${GH_AW_HOME}/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -348,180 +354,44 @@ jobs:
git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
echo "Git configured with standard GitHub Actions identity"
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[Weekly Summary] \". Discussions will be created in category \"audits\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Upload a file as a URL-addressable asset that can be referenced in issues, PRs, or comments. The file is stored on an orphaned git branch and returns a permanent URL. Use this for images, diagrams, or other files that need to be embedded in GitHub content. CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg].",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "path": {
- "description": "Absolute file path to upload (e.g., '/tmp/chart.png'). Must be under the workspace or /tmp directory. By default, only image files (.png, .jpg, .jpeg) are allowed; other file types require workflow configuration.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "path"
- ],
- "type": "object"
- },
- "name": "upload_asset"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[Weekly Summary] \". Discussions will be created in category \"audits\".",
+ "upload_asset": " CONSTRAINTS: Maximum file size: 10240KB. Allowed file extensions: [.png .jpg .jpeg]."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -617,6 +487,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -641,8 +512,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -653,7 +524,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -664,6 +535,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -681,10 +554,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -696,6 +569,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "issues"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -703,6 +582,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -720,7 +606,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -729,7 +616,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,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.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,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.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -760,7 +647,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -798,15 +685,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -816,7 +703,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -833,9 +720,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -844,18 +731,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -945,9 +832,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -970,7 +857,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -997,9 +884,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1045,6 +932,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-weekly-issue-summary"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1060,7 +949,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1085,9 +974,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1099,9 +988,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1123,9 +1012,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1141,9 +1030,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1158,6 +1047,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/weekly-issue-summary"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "weekly-issue-summary"
GH_AW_WORKFLOW_ID: "weekly-issue-summary"
GH_AW_WORKFLOW_NAME: "Weekly Issue Summary"
@@ -1179,7 +1069,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1205,9 +1095,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1224,6 +1114,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: weeklyissuesummary
steps:
- name: Checkout actions folder
@@ -1236,7 +1127,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download cache-memory artifact (default)
id: download_cache_default
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -1281,7 +1172,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1337,8 +1228,8 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/upload_assets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/upload_assets.cjs');
await main();
diff --git a/.github/workflows/weekly-safe-outputs-spec-review.lock.yml b/.github/workflows/weekly-safe-outputs-spec-review.lock.yml
index 11e64cbcda9..09dcca10adf 100644
--- a/.github/workflows/weekly-safe-outputs-spec-review.lock.yml
+++ b/.github/workflows/weekly-safe-outputs-spec-review.lock.yml
@@ -60,7 +60,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -75,7 +75,7 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
@@ -85,11 +85,11 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -107,9 +107,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "weekly-safe-outputs-spec-review.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -124,20 +124,20 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_pull_request, missing_tool, missing_data, noop
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_create_pull_request.md"
cat << 'GH_AW_PROMPT_EOF'
@@ -169,6 +169,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -182,9 +183,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -200,10 +201,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -222,11 +223,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -250,10 +251,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: weeklysafeoutputsspecreview
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -275,13 +277,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -304,175 +310,48 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_pull_request":{"expires":168,"max":1,"title_prefix":"[spec-review] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[spec-review] \". Labels [\"documentation\" \"safe-outputs\" \"automation\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.",
- "type": "string"
- },
- "branch": {
- "description": "Source branch name containing the changes. If omitted, uses the current working branch.",
- "type": "string"
- },
- "draft": {
- "description": "Whether to create the PR as a draft. Draft PRs cannot be merged until marked as ready for review. Use mark_pull_request_as_ready_for_review to convert a draft PR. Default: true.",
- "type": "boolean"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "repo": {
- "description": "Target repository in 'owner/repo' format. For multi-repo workflows where the target repo differs from the workflow repo, this must match a repo in the allowed-repos list or the configured target-repo. If omitted, defaults to the configured target-repo (from safe-outputs config), NOT the workflow repository. In most cases, you should omit this parameter and let the system use the configured default.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_pull_request"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[spec-review] \". Labels [\"documentation\" \"safe-outputs\" \"automation\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_pull_request": {
"defaultMax": 1,
@@ -569,6 +448,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -593,8 +473,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -605,7 +485,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -613,6 +493,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -630,10 +512,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -645,6 +527,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "repos,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -652,6 +540,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -669,7 +564,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -678,7 +574,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -706,7 +602,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -744,15 +640,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -762,7 +658,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -774,14 +670,14 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -790,18 +686,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -877,9 +773,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -902,7 +798,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -929,9 +825,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -975,6 +871,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-weekly-safe-outputs-spec-review"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -990,7 +888,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1015,9 +913,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1029,9 +927,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1054,9 +952,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1072,9 +970,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
- name: Handle Create Pull Request Error
id: handle_create_pr_error
@@ -1087,9 +985,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_create_pr_error.cjs');
await main();
safe_outputs:
@@ -1106,6 +1004,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/weekly-safe-outputs-spec-review"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "weekly-safe-outputs-spec-review"
GH_AW_WORKFLOW_ID: "weekly-safe-outputs-spec-review"
GH_AW_WORKFLOW_NAME: "Weekly Safe Outputs Specification Review"
@@ -1129,7 +1028,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1176,7 +1075,7 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"auto_merge\":false,\"draft\":false,\"expires\":168,\"labels\":[\"documentation\",\"safe-outputs\",\"automation\"],\"max\":1,\"max_patch_size\":1024,\"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\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"title_prefix\":\"[spec-review] \"},\"missing_data\":{},\"missing_tool\":{}}"
@@ -1184,9 +1083,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/weekly-safe-outputs-spec-review.md b/.github/workflows/weekly-safe-outputs-spec-review.md
index 6facc26d6fa..91cc35074d9 100644
--- a/.github/workflows/weekly-safe-outputs-spec-review.md
+++ b/.github/workflows/weekly-safe-outputs-spec-review.md
@@ -180,33 +180,38 @@ If script updates were made, create a pull request using `create pull request`:
**Pull Request Template:**
```markdown
-## Summary
+### Summary
Updates the Safe Outputs conformance checker script to align with recent specification changes.
-## Specification Changes Reviewed
+### Specification Changes Reviewed
[List git commits or specific changes reviewed]
-## Script Updates
+
+📋 Script Updates & Testing Details
-### New Checks Added
+### Script Updates
+
+#### New Checks Added
- **CHECK-ID**: Description of new check and what requirement it validates
-### Checks Modified
+#### Checks Modified
- **CHECK-ID**: Description of modifications and why they were needed
-### Checks Removed
+#### Checks Removed
- **CHECK-ID**: Reason for removal (requirement deprecated/removed)
-## Testing
+### Testing
Ran the updated script successfully:
```
[Include relevant output showing tests passed]
```
-## Related Files
+
+
+### Related Files
- Specification: `docs/src/content/docs/reference/safe-outputs-specification.md`
- Conformance Script: `scripts/check-safe-outputs-conformance.sh`
diff --git a/.github/workflows/workflow-generator.lock.yml b/.github/workflows/workflow-generator.lock.yml
index 8303561c221..8991be26930 100644
--- a/.github/workflows/workflow-generator.lock.yml
+++ b/.github/workflows/workflow-generator.lock.yml
@@ -69,7 +69,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -84,7 +84,7 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
@@ -94,11 +94,11 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -119,9 +119,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/add_reaction.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/add_reaction.cjs');
await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -129,18 +129,18 @@ jobs:
GH_AW_WORKFLOW_FILE: "workflow-generator.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/compute_text.cjs');
await main();
- name: Lock issue for agent workflow
id: lock-issue
@@ -148,9 +148,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/lock-issue.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/lock-issue.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -165,15 +165,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: update_issue, assign_to_agent, missing_tool, missing_data, noop
@@ -207,6 +207,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -220,9 +221,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -239,10 +240,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -262,11 +263,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -289,10 +290,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: workflowgenerator
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -314,13 +316,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -343,238 +349,49 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"assign_to_agent":{"allowed":["copilot"],"max":1,"target":"triggering"},"missing_data":{},"missing_tool":{},"noop":{"max":1},"update_issue":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Assign the GitHub Copilot coding agent to work on an issue or pull request. The agent will analyze the issue/PR and attempt to implement a solution, creating a pull request when complete. Use this to delegate coding tasks to Copilot. Example usage: assign_to_agent(issue_number=123, agent=\"copilot\") or assign_to_agent(pull_number=456, agent=\"copilot\", pull_request_repo=\"owner/repo\") CONSTRAINTS: Maximum 1 issue(s) can be assigned to agent.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "agent": {
- "description": "Agent identifier to assign. Defaults to 'copilot' (the Copilot coding agent) if not specified.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "issue_number": {
- "description": "Issue number to assign the Copilot coding agent to. This is the numeric ID from the GitHub URL (e.g., 234 in github.com/owner/repo/issues/234). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from an issue created earlier in the same workflow run. The issue should contain clear, actionable requirements. Either issue_number or pull_number must be provided, but not both.",
- "type": [
- "number",
- "string"
- ]
- },
- "pull_number": {
- "description": "Pull request number to assign the Copilot coding agent to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/pull/456). Either issue_number or pull_number must be provided, but not both.",
- "type": [
- "number",
- "string"
- ]
- },
- "pull_request_repo": {
- "description": "Target repository where the pull request should be created, in 'owner/repo' format. If omitted, the PR will be created in the same repository as the issue. This allows issues and code to live in different repositories. The global pull-request-repo configuration (if set) is automatically allowed; additional repositories must be listed in allowed-pull-request-repos.",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "assign_to_agent"
- },
- {
- "description": "Update an existing GitHub issue's title, body, labels, assignees, or milestone WITHOUT closing it. This tool is primarily for editing issue metadata and content. While it supports changing status between 'open' and 'closed', use close_issue instead when you want to close an issue with a closing comment. Body updates support replacing, appending to, prepending content, or updating a per-run \"island\" section. CONSTRAINTS: Maximum 1 issue(s) can be updated. Body updates are allowed.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "assignees": {
- "description": "Replace the issue assignees with this list of GitHub usernames (e.g., ['octocat', 'mona']).",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "body": {
- "description": "Issue body content in Markdown. For 'replace', this becomes the entire body. For 'append'/'prepend', this content is added with a separator and an attribution footer. For 'replace-island', only the run-specific section is updated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "issue_number": {
- "description": "Issue number to update. This is the numeric ID from the GitHub URL (e.g., 789 in github.com/owner/repo/issues/789). Required when the workflow target is '*' (any issue).",
- "type": [
- "number",
- "string"
- ]
- },
- "labels": {
- "description": "Replace the issue labels with this list (e.g., ['bug', 'tracking:foo']). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "milestone": {
- "description": "Milestone number to assign (e.g., 1). Use null to clear.",
- "type": [
- "number",
- "string"
- ]
- },
- "operation": {
- "description": "How to update the issue body: 'append' (default - add to end with separator), 'prepend' (add to start with separator), 'replace' (overwrite entire body), or 'replace-island' (update a run-specific section).",
- "enum": [
- "replace",
- "append",
- "prepend",
- "replace-island"
- ],
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "status": {
- "description": "New issue status: 'open' to reopen a closed issue, 'closed' to close an open issue.",
- "enum": [
- "open",
- "closed"
- ],
- "type": "string"
- },
- "title": {
- "description": "New issue title to replace the existing title.",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "update_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "assign_to_agent": " CONSTRAINTS: Maximum 1 issue(s) can be assigned to agent.",
+ "update_issue": " CONSTRAINTS: Maximum 1 issue(s) can be updated. Body updates are allowed."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"assign_to_agent": {
"defaultMax": 1,
@@ -714,6 +531,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -738,8 +556,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -750,7 +568,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -758,6 +576,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -775,10 +595,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -790,6 +610,12 @@ jobs:
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -797,6 +623,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -814,7 +647,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -823,7 +657,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -851,7 +685,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -889,15 +723,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -907,7 +741,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -924,9 +758,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -935,18 +769,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1021,9 +855,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1046,7 +880,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1073,9 +907,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1119,6 +953,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-workflow-generator"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1134,7 +970,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1158,9 +994,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1171,9 +1007,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1195,9 +1031,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1212,9 +1048,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
pre_activation:
@@ -1237,7 +1073,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1246,9 +1082,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
- name: Check user rate limit
id: check_rate_limit
@@ -1261,9 +1097,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_rate_limit.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_rate_limit.cjs');
await main();
safe_outputs:
@@ -1280,6 +1116,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/workflow-generator"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "workflow-generator"
GH_AW_WORKFLOW_NAME: "Workflow Generator"
outputs:
@@ -1303,7 +1140,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1329,9 +1166,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Assign to agent
id: assign_to_agent
@@ -1345,9 +1182,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/assign_to_agent.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/assign_to_agent.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
@@ -1378,15 +1215,15 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Unlock issue after agent workflow
id: unlock-issue
if: ((github.event_name == 'issues') || (github.event_name == 'issue_comment')) && (needs.activation.outputs.issue_locked == 'true')
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/unlock-issue.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/unlock-issue.cjs');
await main();
diff --git a/.github/workflows/workflow-health-manager.lock.yml b/.github/workflows/workflow-health-manager.lock.yml
index 91cfdebbff7..ca181025821 100644
--- a/.github/workflows/workflow-health-manager.lock.yml
+++ b/.github/workflows/workflow-health-manager.lock.yml
@@ -66,7 +66,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -81,18 +81,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -110,9 +110,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "workflow-health-manager.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -128,16 +128,16 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_WIKI_NOTE: ${{ '' }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/repo_memory_prompt.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/repo_memory_prompt.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: add_comment, create_issue, update_issue, missing_tool, missing_data, noop
@@ -171,6 +171,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -187,9 +188,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -212,10 +213,10 @@ jobs:
GH_AW_WIKI_NOTE: ''
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -241,11 +242,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -271,10 +272,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: workflowhealthmanager
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -296,13 +298,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
# Repo memory git-based storage configuration from frontmatter processed below
- name: Clone repo-memory branch (default)
env:
@@ -312,7 +318,7 @@ jobs:
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
- run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh
+ run: bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -335,16 +341,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -353,291 +359,32 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":15},"create_issue":{"expires":24,"group":true,"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":51200}]},"update_issue":{"max":5}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 10 issue(s) can be created. Labels [\"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 15 comment(s) can be added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Update an existing GitHub issue's title, body, labels, assignees, or milestone WITHOUT closing it. This tool is primarily for editing issue metadata and content. While it supports changing status between 'open' and 'closed', use close_issue instead when you want to close an issue with a closing comment. Body updates support replacing, appending to, prepending content, or updating a per-run \"island\" section. CONSTRAINTS: Maximum 5 issue(s) can be updated.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "assignees": {
- "description": "Replace the issue assignees with this list of GitHub usernames (e.g., ['octocat', 'mona']).",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "body": {
- "description": "Issue body content in Markdown. For 'replace', this becomes the entire body. For 'append'/'prepend', this content is added with a separator and an attribution footer. For 'replace-island', only the run-specific section is updated.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "issue_number": {
- "description": "Issue number to update. This is the numeric ID from the GitHub URL (e.g., 789 in github.com/owner/repo/issues/789). Required when the workflow target is '*' (any issue).",
- "type": [
- "number",
- "string"
- ]
- },
- "labels": {
- "description": "Replace the issue labels with this list (e.g., ['bug', 'tracking:foo']). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "milestone": {
- "description": "Milestone number to assign (e.g., 1). Use null to clear.",
- "type": [
- "number",
- "string"
- ]
- },
- "operation": {
- "description": "How to update the issue body: 'append' (default - add to end with separator), 'prepend' (add to start with separator), 'replace' (overwrite entire body), or 'replace-island' (update a run-specific section).",
- "enum": [
- "replace",
- "append",
- "prepend",
- "replace-island"
- ],
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "status": {
- "description": "New issue status: 'open' to reopen a closed issue, 'closed' to close an open issue.",
- "enum": [
- "open",
- "closed"
- ],
- "type": "string"
- },
- "title": {
- "description": "New issue title to replace the existing title.",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "update_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 15 comment(s) can be added.",
+ "create_issue": " CONSTRAINTS: Maximum 10 issue(s) can be created. Labels [\"cookie\"] will be automatically added.",
+ "update_issue": " CONSTRAINTS: Maximum 5 issue(s) can be updated."
},
- {
- "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "memory_id": {
- "description": "Memory identifier to validate. Defaults to 'default' if not specified.",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "push_repo_memory"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"add_comment": {
"defaultMax": 1,
@@ -803,6 +550,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -827,8 +575,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -839,7 +587,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -847,7 +595,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -865,10 +614,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -876,10 +625,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests,actions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -887,6 +641,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -904,7 +665,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -913,7 +675,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -941,7 +703,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -979,15 +741,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -997,7 +759,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -1014,9 +776,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -1025,18 +787,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -1120,9 +882,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1145,7 +907,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1172,9 +934,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1220,6 +982,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-workflow-health-manager"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1235,7 +999,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1259,9 +1023,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1272,9 +1036,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1298,9 +1062,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1315,9 +1079,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
pre_activation:
@@ -1338,7 +1102,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1347,9 +1111,9 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
push_repo_memory:
@@ -1361,6 +1125,8 @@ jobs:
concurrency:
group: "push-repo-memory-${{ github.repository }}"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
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 }}
@@ -1376,7 +1142,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1419,9 +1185,9 @@ jobs:
FILE_GLOB_FILTER: "**"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs');
await main();
safe_outputs:
@@ -1437,6 +1203,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/workflow-health-manager"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "workflow-health-manager"
GH_AW_WORKFLOW_NAME: "Workflow Health Manager - Meta-Orchestrator"
outputs:
@@ -1461,7 +1228,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1487,9 +1254,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/workflow-normalizer.lock.yml b/.github/workflows/workflow-normalizer.lock.yml
index 98679ab54ee..9213a40a436 100644
--- a/.github/workflows/workflow-normalizer.lock.yml
+++ b/.github/workflows/workflow-normalizer.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","python","node"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "workflow-normalizer.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,15 +125,16 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, missing_tool, missing_data, noop
@@ -167,6 +168,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -184,9 +186,9 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -202,10 +204,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -224,11 +226,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -254,10 +256,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: workflownormalizer
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -279,7 +282,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -315,7 +318,11 @@ jobs:
build-args: |
BINARY=dist/gh-aw-linux-amd64
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -338,16 +345,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -356,10 +363,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -374,173 +381,36 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
fi
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_issue":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[workflow-style] \". Labels [\"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[workflow-style] \". Labels [\"cookie\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_issue": {
"defaultMax": 1,
@@ -634,6 +504,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -658,8 +529,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -670,7 +541,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -678,7 +549,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -697,10 +569,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -713,6 +585,13 @@ jobs:
"GITHUB_TOKEN": "\${GITHUB_TOKEN}",
"GITHUB_ACTOR": "\${GITHUB_ACTOR}",
"GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
@@ -720,10 +599,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -731,6 +615,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -748,7 +639,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -757,7 +649,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,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.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,cdn.jsdelivr.net,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,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,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.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -785,7 +677,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -823,15 +715,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -841,7 +733,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -858,9 +750,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -869,18 +761,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -955,9 +847,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -980,7 +872,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1007,9 +899,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1052,6 +944,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-workflow-normalizer"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1067,7 +961,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1092,9 +986,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1106,9 +1000,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1129,9 +1023,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1147,9 +1041,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1163,6 +1057,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/workflow-normalizer"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_TRACKER_ID: "workflow-normalizer"
GH_AW_WORKFLOW_ID: "workflow-normalizer"
GH_AW_WORKFLOW_NAME: "Workflow Normalizer"
@@ -1186,7 +1081,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1212,9 +1107,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.github/workflows/workflow-skill-extractor.lock.yml b/.github/workflows/workflow-skill-extractor.lock.yml
index b8880774e1c..61d3e030a86 100644
--- a/.github/workflows/workflow-skill-extractor.lock.yml
+++ b/.github/workflows/workflow-skill-extractor.lock.yml
@@ -64,7 +64,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -79,18 +79,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -108,9 +108,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "workflow-skill-extractor.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -125,15 +125,15 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
Tools: create_issue, create_discussion, missing_tool, missing_data, noop
@@ -167,6 +167,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -183,9 +184,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -201,10 +202,10 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -223,11 +224,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -253,10 +254,11 @@ jobs:
GH_AW_ASSETS_ALLOWED_EXTS: ""
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_HOME }}/safeoutputs/outputs.jsonl
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: workflowskillextractor
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -278,13 +280,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -307,16 +313,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -325,201 +331,31 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- name: Write Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p ${GH_AW_HOME}/safeoutputs
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
+ cat > ${GH_AW_HOME}/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"create_discussion":{"expires":168,"max":1},"create_issue":{"expires":48,"group":true,"max":3},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- name: Write Safe Outputs Tools
run: |
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 3 issue(s) can be created. Title will be prefixed with \"[refactoring] \". Labels [\"refactoring\" \"shared-component\" \"improvement\" \"cookie\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Create a GitHub discussion for announcements, Q\u0026A, reports, status updates, or community conversations. Use this for content that benefits from threaded replies, doesn't require task tracking, or serves as documentation. For actionable work items that need assignment and status tracking, use create_issue instead. CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"reports\".",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Discussion content in Markdown. Do NOT repeat the title as a heading since it already appears as the discussion's h1. Include all relevant context, findings, or questions.",
- "type": "string"
- },
- "category": {
- "description": "Discussion category by name (e.g., 'General'), slug (e.g., 'general'), or ID. If omitted, uses the first available category. Category must exist in the repository.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "title": {
- "description": "Concise discussion title summarizing the topic. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_discussion"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
+ cat > ${GH_AW_HOME}/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Discussions will be created in category \"reports\".",
+ "create_issue": " CONSTRAINTS: Maximum 3 issue(s) can be created. Title will be prefixed with \"[refactoring] \". Labels [\"refactoring\" \"shared-component\" \"improvement\" \"cookie\"] will be automatically added."
},
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
+ cat > ${GH_AW_HOME}/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
{
"create_discussion": {
"defaultMax": 1,
@@ -639,6 +475,7 @@ jobs:
}
}
GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ node ${GH_AW_HOME}/actions/generate_safe_outputs_tools.cjs
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -663,8 +500,8 @@ jobs:
DEBUG: '*'
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ env.GH_AW_HOME }}/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
@@ -675,7 +512,7 @@ jobs:
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash ${GH_AW_HOME}/actions/start_safe_outputs_server.sh
- name: Start MCP Gateway
id: start-mcp-gateway
@@ -683,7 +520,8 @@ jobs:
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -701,10 +539,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -712,10 +550,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -723,6 +566,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -740,7 +590,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -769,7 +620,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat *)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find .github/workflows -name '\''\'\'''\''*.md'\''\'\'''\'')'\'' --allow-tool '\''shell(grep -r '\''\'\'''\''*'\''\'\'''\'' .github/workflows)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls *)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc *)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -797,7 +648,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -835,15 +686,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -853,7 +704,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Copy Safe Outputs
if: always()
run: |
@@ -870,9 +721,9 @@ jobs:
GITHUB_API_URL: ${{ github.api_url }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/collect_ndjson_output.cjs');
await main();
- name: Parse agent logs for step summary
if: always()
@@ -881,18 +732,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -967,9 +818,9 @@ jobs:
HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -992,7 +843,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -1019,9 +870,9 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs');
await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
@@ -1065,6 +916,8 @@ jobs:
concurrency:
group: "gh-aw-conclusion-workflow-skill-extractor"
cancel-in-progress: false
+ env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
@@ -1080,7 +933,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1104,9 +957,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/noop.cjs');
await main();
- name: Record Missing Tool
id: missing_tool
@@ -1117,9 +970,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/missing_tool.cjs');
await main();
- name: Handle Agent Failure
id: handle_agent_failure
@@ -1141,9 +994,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_agent_failure.cjs');
await main();
- name: Handle No-Op Message
id: handle_noop_message
@@ -1158,9 +1011,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/handle_noop_message.cjs');
await main();
safe_outputs:
@@ -1175,6 +1028,7 @@ jobs:
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/workflow-skill-extractor"
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID: "workflow-skill-extractor"
GH_AW_WORKFLOW_NAME: "Workflow Skill Extractor"
outputs:
@@ -1197,7 +1051,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
@@ -1223,9 +1077,9 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/safe_output_handler_manager.cjs');
await main();
- name: Upload Safe Output Items Manifest
if: always()
diff --git a/.nvmrc b/.nvmrc
index 209e3ef4b62..a45fd52cc58 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-20
+24
diff --git a/actions/setup/js/add_reaction.cjs b/actions/setup/js/add_reaction.cjs
index c3e95d3081d..163fa40bad5 100644
--- a/actions/setup/js/add_reaction.cjs
+++ b/actions/setup/js/add_reaction.cjs
@@ -4,6 +4,18 @@
const { getErrorMessage, isLockedError } = require("./error_helpers.cjs");
const { ERR_API, ERR_NOT_FOUND, ERR_VALIDATION } = require("./error_codes.cjs");
+/** Maps REST reaction names to GraphQL ReactionContent enum values */
+const REACTION_MAP = {
+ "+1": "THUMBS_UP",
+ "-1": "THUMBS_DOWN",
+ laugh: "LAUGH",
+ confused: "CONFUSED",
+ heart: "HEART",
+ hooray: "HOORAY",
+ rocket: "ROCKET",
+ eyes: "EYES",
+};
+
/**
* Add a reaction to the triggering item (issue, PR, comment, or discussion).
* This provides immediate feedback to the user when a workflow is triggered.
@@ -17,7 +29,7 @@ async function main() {
core.info(`Adding reaction: ${reaction}`);
// Validate reaction type
- const validReactions = ["+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", "eyes"];
+ const validReactions = Object.keys(REACTION_MAP);
if (!validReactions.includes(reaction)) {
core.setFailed(`${ERR_VALIDATION}: Invalid reaction type: ${reaction}. Valid reactions are: ${validReactions.join(", ")}`);
return;
@@ -101,7 +113,7 @@ async function main() {
// Add reaction using REST API (for non-discussion events)
core.info(`Adding reaction to: ${reactionEndpoint}`);
- await addReaction(reactionEndpoint, reaction);
+ await addReaction(/** @type {string} */ reactionEndpoint, reaction);
} catch (error) {
const errorMessage = getErrorMessage(error);
@@ -124,7 +136,7 @@ async function main() {
* @param {string} reaction - The reaction type to add
*/
async function addReaction(endpoint, reaction) {
- const response = await github.request("POST " + endpoint, {
+ const response = await github.request(`POST ${endpoint}`, {
content: reaction,
headers: {
Accept: "application/vnd.github+json",
@@ -132,13 +144,8 @@ async function addReaction(endpoint, reaction) {
});
const reactionId = response.data?.id;
- if (reactionId) {
- core.info(`Successfully added reaction: ${reaction} (id: ${reactionId})`);
- core.setOutput("reaction-id", reactionId.toString());
- } else {
- core.info(`Successfully added reaction: ${reaction}`);
- core.setOutput("reaction-id", "");
- }
+ core.info(`Successfully added reaction: ${reaction}${reactionId ? ` (id: ${reactionId})` : ""}`);
+ core.setOutput("reaction-id", reactionId?.toString() ?? "");
}
/**
@@ -147,19 +154,7 @@ async function addReaction(endpoint, reaction) {
* @param {string} reaction - The reaction type to add (mapped to GitHub's ReactionContent enum)
*/
async function addDiscussionReaction(subjectId, reaction) {
- // Map reaction names to GitHub's GraphQL ReactionContent enum
- const reactionMap = {
- "+1": "THUMBS_UP",
- "-1": "THUMBS_DOWN",
- laugh: "LAUGH",
- confused: "CONFUSED",
- heart: "HEART",
- hooray: "HOORAY",
- rocket: "ROCKET",
- eyes: "EYES",
- };
-
- const reactionContent = reactionMap[reaction];
+ const reactionContent = REACTION_MAP[reaction];
if (!reactionContent) {
throw new Error(`${ERR_VALIDATION}: Invalid reaction type for GraphQL: ${reaction}`);
}
diff --git a/actions/setup/js/add_reviewer.cjs b/actions/setup/js/add_reviewer.cjs
index 043a3f7f5d6..4ad267b79db 100644
--- a/actions/setup/js/add_reviewer.cjs
+++ b/actions/setup/js/add_reviewer.cjs
@@ -14,9 +14,6 @@ const { createAuthenticatedGitHubClient } = require("./handler_auth.cjs");
// GitHub Copilot reviewer bot username
const COPILOT_REVIEWER_BOT = "copilot-pull-request-reviewer[bot]";
-/** @type {string} Safe output type handled by this module */
-const HANDLER_TYPE = "add_reviewer";
-
/**
* Main handler factory for add_reviewer
* Returns a message handler function that processes individual add_reviewer messages
diff --git a/actions/setup/js/add_reviewer.test.cjs b/actions/setup/js/add_reviewer.test.cjs
index 06b59e8c191..b4a06580bd8 100644
--- a/actions/setup/js/add_reviewer.test.cjs
+++ b/actions/setup/js/add_reviewer.test.cjs
@@ -286,4 +286,175 @@ describe("add_reviewer (Handler Factory Architecture)", () => {
expect(result.reviewersAdded).toEqual(["user1", "copilot"]);
expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Failed to add copilot as reviewer"));
});
+
+ it("should handle undefined reviewers as empty array", async () => {
+ const message = {
+ type: "add_reviewer",
+ // reviewers intentionally omitted
+ };
+
+ const result = await handler(message, {});
+
+ expect(result.success).toBe(true);
+ expect(result.reviewersAdded).toEqual([]);
+ expect(result.message).toContain("No valid reviewers found");
+ expect(mockGithub.rest.pulls.requestReviewers).not.toHaveBeenCalled();
+ });
+
+ it("should add only copilot when copilot is the only reviewer", async () => {
+ const message = {
+ type: "add_reviewer",
+ reviewers: ["copilot"],
+ };
+
+ const result = await handler(message, {});
+
+ expect(result.success).toBe(true);
+ expect(result.reviewersAdded).toEqual(["copilot"]);
+ // Only called once for copilot, not for other reviewers
+ expect(mockGithub.rest.pulls.requestReviewers).toHaveBeenCalledTimes(1);
+ expect(mockGithub.rest.pulls.requestReviewers).toHaveBeenCalledWith({
+ owner: "testowner",
+ repo: "testrepo",
+ pull_number: 123,
+ reviewers: ["copilot-pull-request-reviewer[bot]"],
+ });
+ });
+
+ it("should use default config values when no options provided", async () => {
+ const { main } = require("./add_reviewer.cjs");
+ const defaultHandler = await main({});
+
+ const message = {
+ type: "add_reviewer",
+ reviewers: ["anyuser"],
+ };
+
+ // With no allowed list, all reviewers are allowed
+ const result = await defaultHandler(message, {});
+
+ expect(result.success).toBe(true);
+ expect(result.reviewersAdded).toEqual(["anyuser"]);
+ expect(mockGithub.rest.pulls.requestReviewers).toHaveBeenCalledWith({
+ owner: "testowner",
+ repo: "testrepo",
+ pull_number: 123,
+ reviewers: ["anyuser"],
+ });
+ });
+
+ it("should return error for invalid pull_request_number", async () => {
+ const invalidValues = ["not-a-number", null, "abc123"];
+
+ for (const invalidValue of invalidValues) {
+ vi.clearAllMocks();
+ const message = {
+ type: "add_reviewer",
+ reviewers: ["user1"],
+ pull_request_number: invalidValue,
+ };
+
+ const result = await handler(message, {});
+
+ expect(result.success).toBe(false);
+ expect(result.error).toContain("Invalid pull_request_number");
+ expect(mockGithub.rest.pulls.requestReviewers).not.toHaveBeenCalled();
+ }
+ });
+
+ it("should filter out copilot when not in allowed list", async () => {
+ const { main } = require("./add_reviewer.cjs");
+ // allowed list does NOT include "copilot"
+ const restrictedHandler = await main({ max: 10, allowed: ["user1", "user2"] });
+
+ const message = {
+ type: "add_reviewer",
+ reviewers: ["user1", "copilot"],
+ };
+
+ const result = await restrictedHandler(message, {});
+
+ expect(result.success).toBe(true);
+ expect(result.reviewersAdded).toEqual(["user1"]);
+ // Only called once — for user1 only; copilot was filtered out
+ expect(mockGithub.rest.pulls.requestReviewers).toHaveBeenCalledTimes(1);
+ expect(mockGithub.rest.pulls.requestReviewers).toHaveBeenCalledWith({
+ owner: "testowner",
+ repo: "testrepo",
+ pull_number: 123,
+ reviewers: ["user1"],
+ });
+ });
+
+ it("should sanitize null and falsy values in reviewers array", async () => {
+ const { main } = require("./add_reviewer.cjs");
+ const permissiveHandler = await main({ max: 10, allowed: [] });
+
+ const message = {
+ type: "add_reviewer",
+ // null, undefined, false, empty string, and whitespace-only strings are all stripped
+ reviewers: [null, undefined, false, "", " ", "user1", " user2 "],
+ };
+
+ const result = await permissiveHandler(message, {});
+
+ expect(result.success).toBe(true);
+ // Only real usernames survive; whitespace is trimmed; whitespace-only strings are dropped
+ expect(result.reviewersAdded).toEqual(["user1", "user2"]);
+ expect(mockGithub.rest.pulls.requestReviewers).toHaveBeenCalledWith({
+ owner: "testowner",
+ repo: "testrepo",
+ pull_number: 123,
+ reviewers: ["user1", "user2"],
+ });
+ });
+
+ it("should trim reviewer list to maxCount within a single message", async () => {
+ const { main } = require("./add_reviewer.cjs");
+ const tightHandler = await main({ max: 2, allowed: [] });
+
+ const message = {
+ type: "add_reviewer",
+ reviewers: ["user1", "user2", "user3", "user4"],
+ };
+
+ const result = await tightHandler(message, {});
+
+ expect(result.success).toBe(true);
+ // Only first 2 reviewers should be added
+ expect(result.reviewersAdded).toEqual(["user1", "user2"]);
+ expect(mockGithub.rest.pulls.requestReviewers).toHaveBeenCalledWith({
+ owner: "testowner",
+ repo: "testrepo",
+ pull_number: 123,
+ reviewers: ["user1", "user2"],
+ });
+ });
+
+ it("should count staged calls toward max processedCount", async () => {
+ const originalEnv = process.env.GH_AW_SAFE_OUTPUTS_STAGED;
+ process.env.GH_AW_SAFE_OUTPUTS_STAGED = "true";
+
+ try {
+ const { main } = require("./add_reviewer.cjs");
+ const stagedHandler = await main({ max: 1, allowed: ["user1"] });
+
+ const message = {
+ type: "add_reviewer",
+ reviewers: ["user1"],
+ };
+
+ // First call in staged mode — should succeed as a preview
+ const result1 = await stagedHandler(message, {});
+ expect(result1.success).toBe(true);
+ expect(result1.staged).toBe(true);
+
+ // Second call should be blocked by max count (processedCount was incremented in staged mode)
+ const result2 = await stagedHandler(message, {});
+ expect(result2.success).toBe(false);
+ expect(result2.error).toContain("Max count");
+ } finally {
+ process.env.GH_AW_SAFE_OUTPUTS_STAGED = originalEnv;
+ }
+ });
});
diff --git a/actions/setup/js/check_command_position.cjs b/actions/setup/js/check_command_position.cjs
index 9feabed4dbf..5c8b6ef04a1 100644
--- a/actions/setup/js/check_command_position.cjs
+++ b/actions/setup/js/check_command_position.cjs
@@ -41,6 +41,18 @@ async function main() {
const eventName = context.eventName;
try {
+ // For labeled events (label-command triggers), skip the command position check.
+ // The label name itself is the trigger, not a slash command in the body.
+ // Label name matching is enforced by the workflow-level `if:` condition
+ // (e.g. github.event.label.name == 'cloclo'), so no additional filtering
+ // is needed here regardless of which labels are configured.
+ if (context.payload?.action === "labeled") {
+ core.info(`Event ${eventName} with action 'labeled' does not require command position check`);
+ core.setOutput("command_position_ok", "true");
+ core.setOutput("matched_command", "");
+ return;
+ }
+
if (eventName === "issues") {
text = context.payload.issue?.body || "";
} else if (eventName === "pull_request") {
diff --git a/actions/setup/js/check_command_position.test.cjs b/actions/setup/js/check_command_position.test.cjs
index 636c1c12da6..c447337fade 100644
--- a/actions/setup/js/check_command_position.test.cjs
+++ b/actions/setup/js/check_command_position.test.cjs
@@ -114,5 +114,29 @@ const mockCore = {
(mockContext.payload = { comment: { body: "/discuss-bot analyze this" } }),
await eval(`(async () => { ${checkCommandPositionScript}; await main(); })()`),
expect(mockCore.setOutput).toHaveBeenCalledWith("command_position_ok", "true"));
+ }),
+ it("should pass for labeled issues event (label-command trigger)", async () => {
+ process.env.GH_AW_COMMANDS = JSON.stringify(["cloclo"]);
+ mockContext.eventName = "issues";
+ mockContext.payload = { action: "labeled", label: { name: "cloclo" }, issue: { body: "This is a regular issue body" } };
+ await eval(`(async () => { ${checkCommandPositionScript}; await main(); })()`);
+ expect(mockCore.setOutput).toHaveBeenCalledWith("command_position_ok", "true");
+ expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("does not require command position check"));
+ }),
+ it("should pass for labeled pull_request event (label-command trigger)", async () => {
+ process.env.GH_AW_COMMANDS = JSON.stringify(["cloclo"]);
+ mockContext.eventName = "pull_request";
+ mockContext.payload = { action: "labeled", label: { name: "cloclo" }, pull_request: { body: "PR body without command" } };
+ await eval(`(async () => { ${checkCommandPositionScript}; await main(); })()`);
+ expect(mockCore.setOutput).toHaveBeenCalledWith("command_position_ok", "true");
+ expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("does not require command position check"));
+ }),
+ it("should pass for labeled discussion event (label-command trigger)", async () => {
+ process.env.GH_AW_COMMANDS = JSON.stringify(["cloclo"]);
+ mockContext.eventName = "discussion";
+ mockContext.payload = { action: "labeled", label: { name: "cloclo" }, discussion: { body: "Discussion body without command" } };
+ await eval(`(async () => { ${checkCommandPositionScript}; await main(); })()`);
+ expect(mockCore.setOutput).toHaveBeenCalledWith("command_position_ok", "true");
+ expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("does not require command position check"));
}));
}));
diff --git a/actions/setup/js/check_membership.cjs b/actions/setup/js/check_membership.cjs
index 699d38bb419..49103a93e6b 100644
--- a/actions/setup/js/check_membership.cjs
+++ b/actions/setup/js/check_membership.cjs
@@ -52,19 +52,14 @@ async function main() {
// Check if the actor has the required repository permissions
const result = await checkRepositoryPermission(actor, owner, repo, requiredPermissions);
- if (result.error) {
- core.setOutput("is_team_member", "false");
- core.setOutput("result", "api_error");
- core.setOutput("error_message", `Repository permission check failed: ${result.error}`);
- return;
- }
-
if (result.authorized) {
core.setOutput("is_team_member", "true");
core.setOutput("result", "authorized");
core.setOutput("user_permission", result.permission);
} else {
- // User doesn't have required permissions, check if they're an allowed bot
+ // User doesn't have required permissions (or the permission check failed with an error).
+ // Always attempt the bot allowlist fallback before giving up, so that GitHub Apps whose
+ // actor is not a recognized GitHub user (e.g. "Copilot") are not silently denied.
if (allowedBots && allowedBots.length > 0) {
core.info(`Checking if actor '${actor}' is in allowed bots list: ${allowedBots.join(", ")}`);
@@ -84,7 +79,7 @@ async function main() {
core.warning(`Bot '${actor}' is in the allowed list but not active/installed on ${owner}/${repo}`);
core.setOutput("is_team_member", "false");
core.setOutput("result", "bot_not_active");
- core.setOutput("user_permission", result.permission);
+ core.setOutput("user_permission", result.permission ?? "bot");
core.setOutput("error_message", `Access denied: Bot '${actor}' is not active/installed on this repository`);
return;
} else {
@@ -94,14 +89,20 @@ async function main() {
}
// Not authorized by role or bot
- core.setOutput("is_team_member", "false");
- core.setOutput("result", "insufficient_permissions");
- core.setOutput("user_permission", result.permission);
- core.setOutput(
- "error_message",
- `Access denied: User '${actor}' is not authorized. Required permissions: ${requiredPermissions.join(", ")}. ` +
- `To allow this user to run the workflow, add their role to the frontmatter. Example: roles: [${requiredPermissions.join(", ")}, ${result.permission}]`
- );
+ if (result.error) {
+ core.setOutput("is_team_member", "false");
+ core.setOutput("result", "api_error");
+ core.setOutput("error_message", `Repository permission check failed: ${result.error}`);
+ } else {
+ core.setOutput("is_team_member", "false");
+ core.setOutput("result", "insufficient_permissions");
+ core.setOutput("user_permission", result.permission);
+ core.setOutput(
+ "error_message",
+ `Access denied: User '${actor}' is not authorized. Required permissions: ${requiredPermissions.join(", ")}. ` +
+ `To allow this user to run the workflow, add their role to the frontmatter. Example: roles: [${requiredPermissions.join(", ")}, ${result.permission}]`
+ );
+ }
}
}
diff --git a/actions/setup/js/check_membership.test.cjs b/actions/setup/js/check_membership.test.cjs
index 81d61457d3b..9cd490489f5 100644
--- a/actions/setup/js/check_membership.test.cjs
+++ b/actions/setup/js/check_membership.test.cjs
@@ -381,6 +381,51 @@ describe("check_membership.cjs", () => {
expect(mockCore.setOutput).toHaveBeenCalledWith("result", "insufficient_permissions");
});
+ it("should authorize a bot in the allowlist when permission check returns an API error (e.g. GitHub App not a user)", async () => {
+ process.env.GH_AW_ALLOWED_BOTS = "Copilot";
+ mockContext.actor = "Copilot";
+
+ const notAUserError = new Error("Copilot is not a user");
+ mockGithub.rest.repos.getCollaboratorPermissionLevel
+ .mockRejectedValueOnce(notAUserError) // initial permission check → error
+ .mockResolvedValueOnce({ data: { permission: "none" } }); // bot status check (Copilot[bot] form) → active
+
+ await runScript();
+
+ expect(mockCore.setOutput).toHaveBeenCalledWith("is_team_member", "true");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("result", "authorized_bot");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("user_permission", "bot");
+ });
+
+ it("should return bot_not_active when permission check returns API error and bot is not installed", async () => {
+ process.env.GH_AW_ALLOWED_BOTS = "Copilot";
+ mockContext.actor = "Copilot";
+
+ const notAUserError = new Error("Copilot is not a user");
+ const notFoundError = { status: 404, message: "Not Found" };
+ mockGithub.rest.repos.getCollaboratorPermissionLevel
+ .mockRejectedValueOnce(notAUserError) // initial permission check → error
+ .mockRejectedValue(notFoundError); // all bot status checks → 404
+
+ await runScript();
+
+ expect(mockCore.setOutput).toHaveBeenCalledWith("is_team_member", "false");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("result", "bot_not_active");
+ });
+
+ it("should return api_error when permission check fails and actor is not in allowed bots list", async () => {
+ process.env.GH_AW_ALLOWED_BOTS = "some-other-bot";
+ mockContext.actor = "Copilot";
+
+ const notAUserError = new Error("Copilot is not a user");
+ mockGithub.rest.repos.getCollaboratorPermissionLevel.mockRejectedValue(notAUserError);
+
+ await runScript();
+
+ expect(mockCore.setOutput).toHaveBeenCalledWith("is_team_member", "false");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("result", "api_error");
+ });
+
it("should authorize a bot with [bot] suffix in the allowlist via slug fallback", async () => {
process.env.GH_AW_ALLOWED_BOTS = "copilot";
mockContext.actor = "copilot[bot]";
diff --git a/actions/setup/js/create_pull_request.cjs b/actions/setup/js/create_pull_request.cjs
index a6f52827628..b17f6f7b27f 100644
--- a/actions/setup/js/create_pull_request.cjs
+++ b/actions/setup/js/create_pull_request.cjs
@@ -36,6 +36,9 @@ const HANDLER_TYPE = "create_pull_request";
/** @type {string} Label always added to fallback issues so the triage system can find them */
const MANAGED_FALLBACK_ISSUE_LABEL = "agentic-workflows";
+// GitHub Copilot reviewer bot username
+const COPILOT_REVIEWER_BOT = "copilot-pull-request-reviewer[bot]";
+
/**
* Merges the required fallback label with any workflow-configured labels,
* deduplicating and filtering empty values.
@@ -117,6 +120,7 @@ async function main(config = {}) {
// Extract configuration
const titlePrefix = config.title_prefix || "";
const envLabels = config.labels ? (Array.isArray(config.labels) ? config.labels : config.labels.split(",")).map(label => String(label).trim()).filter(label => label) : [];
+ const configReviewers = config.reviewers ? (Array.isArray(config.reviewers) ? config.reviewers : config.reviewers.split(",")).map(r => String(r).trim()).filter(r => r) : [];
const draftDefault = parseBoolTemplatable(config.draft, true);
const ifNoChanges = config.if_no_changes || "warn";
const allowEmpty = parseBoolTemplatable(config.allow_empty, false);
@@ -166,6 +170,9 @@ async function main(config = {}) {
if (envLabels.length > 0) {
core.info(`Default labels: ${envLabels.join(", ")}`);
}
+ if (configReviewers.length > 0) {
+ core.info(`Configured reviewers: ${configReviewers.join(", ")}`);
+ }
if (titlePrefix) {
core.info(`Title prefix: ${titlePrefix}`);
}
@@ -1030,6 +1037,42 @@ ${patchPreview}`;
core.info(`Added labels to pull request: ${JSON.stringify(labels)}`);
}
+ // Add configured reviewers if specified
+ if (configReviewers.length > 0) {
+ const hasCopilot = configReviewers.includes("copilot");
+ const otherReviewers = configReviewers.filter(r => r !== "copilot");
+
+ if (otherReviewers.length > 0) {
+ core.info(`Requesting ${otherReviewers.length} reviewer(s) for pull request #${pullRequest.number}: ${JSON.stringify(otherReviewers)}`);
+ try {
+ await githubClient.rest.pulls.requestReviewers({
+ owner: repoParts.owner,
+ repo: repoParts.repo,
+ pull_number: pullRequest.number,
+ reviewers: otherReviewers,
+ });
+ core.info(`Requested reviewers for pull request #${pullRequest.number}: ${JSON.stringify(otherReviewers)}`);
+ } catch (reviewerError) {
+ core.warning(`Failed to request reviewers for PR #${pullRequest.number}: ${reviewerError instanceof Error ? reviewerError.message : String(reviewerError)}`);
+ }
+ }
+
+ if (hasCopilot) {
+ core.info(`Requesting copilot as reviewer for pull request #${pullRequest.number}`);
+ try {
+ await githubClient.rest.pulls.requestReviewers({
+ owner: repoParts.owner,
+ repo: repoParts.repo,
+ pull_number: pullRequest.number,
+ reviewers: [COPILOT_REVIEWER_BOT],
+ });
+ core.info(`Requested copilot as reviewer for pull request #${pullRequest.number}`);
+ } catch (copilotError) {
+ core.warning(`Failed to request copilot as reviewer for PR #${pullRequest.number}: ${copilotError instanceof Error ? copilotError.message : String(copilotError)}`);
+ }
+ }
+ }
+
// Enable auto-merge if configured
if (autoMerge) {
try {
diff --git a/actions/setup/js/create_pull_request.test.cjs b/actions/setup/js/create_pull_request.test.cjs
index 6959948d87d..337ec29467a 100644
--- a/actions/setup/js/create_pull_request.test.cjs
+++ b/actions/setup/js/create_pull_request.test.cjs
@@ -758,3 +758,138 @@ ${diffs}
expect(result.error || "").not.toContain("outside the allowed-files list");
});
});
+
+describe("create_pull_request - configured reviewers", () => {
+ let tempDir;
+ let originalEnv;
+
+ beforeEach(() => {
+ originalEnv = { ...process.env };
+ process.env.GH_AW_WORKFLOW_ID = "test-workflow";
+ process.env.GITHUB_REPOSITORY = "test-owner/test-repo";
+ process.env.GITHUB_BASE_REF = "main";
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "create-pr-reviewer-test-"));
+
+ global.core = {
+ info: vi.fn(),
+ warning: vi.fn(),
+ error: vi.fn(),
+ debug: vi.fn(),
+ setFailed: vi.fn(),
+ setOutput: vi.fn(),
+ startGroup: vi.fn(),
+ endGroup: vi.fn(),
+ summary: {
+ addRaw: vi.fn().mockReturnThis(),
+ write: vi.fn().mockResolvedValue(undefined),
+ },
+ };
+ global.github = {
+ rest: {
+ pulls: {
+ create: vi.fn().mockResolvedValue({ data: { number: 42, html_url: "https://github.com/test/pull/42", node_id: "PR_42" } }),
+ requestReviewers: vi.fn().mockResolvedValue({}),
+ },
+ repos: {
+ get: vi.fn().mockResolvedValue({ data: { default_branch: "main" } }),
+ },
+ issues: {
+ addLabels: vi.fn().mockResolvedValue({}),
+ },
+ },
+ graphql: vi.fn(),
+ };
+ global.context = {
+ eventName: "workflow_dispatch",
+ repo: { owner: "test-owner", repo: "test-repo" },
+ payload: {},
+ };
+ global.exec = {
+ exec: vi.fn().mockResolvedValue(0),
+ getExecOutput: vi.fn().mockResolvedValue({ exitCode: 0, stdout: "", stderr: "" }),
+ };
+
+ delete require.cache[require.resolve("./create_pull_request.cjs")];
+ });
+
+ afterEach(() => {
+ for (const key of Object.keys(process.env)) {
+ if (!(key in originalEnv)) {
+ delete process.env[key];
+ }
+ }
+ Object.assign(process.env, originalEnv);
+
+ if (tempDir && fs.existsSync(tempDir)) {
+ fs.rmSync(tempDir, { recursive: true, force: true });
+ }
+
+ delete global.core;
+ delete global.github;
+ delete global.context;
+ delete global.exec;
+ vi.clearAllMocks();
+ });
+
+ it("should request configured reviewers after creating the PR", async () => {
+ const { main } = require("./create_pull_request.cjs");
+ const handler = await main({ reviewers: ["user1", "user2"], allow_empty: true });
+
+ const result = await handler({ title: "Test PR", body: "Test body" }, {});
+
+ expect(result.success).toBe(true);
+ expect(global.github.rest.pulls.requestReviewers).toHaveBeenCalledWith(
+ expect.objectContaining({
+ owner: "test-owner",
+ repo: "test-repo",
+ pull_number: 42,
+ reviewers: ["user1", "user2"],
+ })
+ );
+ });
+
+ it("should handle copilot reviewer separately from regular reviewers", async () => {
+ const { main } = require("./create_pull_request.cjs");
+ const handler = await main({ reviewers: ["user1", "copilot"], allow_empty: true });
+
+ const result = await handler({ title: "Test PR", body: "Test body" }, {});
+
+ expect(result.success).toBe(true);
+ // Should be called twice: once for regular reviewers, once for copilot bot
+ expect(global.github.rest.pulls.requestReviewers).toHaveBeenCalledTimes(2);
+ expect(global.github.rest.pulls.requestReviewers).toHaveBeenCalledWith(expect.objectContaining({ reviewers: ["user1"] }));
+ expect(global.github.rest.pulls.requestReviewers).toHaveBeenCalledWith(expect.objectContaining({ reviewers: ["copilot-pull-request-reviewer[bot]"] }));
+ });
+
+ it("should not call requestReviewers when no reviewers are configured", async () => {
+ const { main } = require("./create_pull_request.cjs");
+ const handler = await main({ allow_empty: true });
+
+ const result = await handler({ title: "Test PR", body: "Test body" }, {});
+
+ expect(result.success).toBe(true);
+ expect(global.github.rest.pulls.requestReviewers).not.toHaveBeenCalled();
+ });
+
+ it("should continue successfully even if requestReviewers fails", async () => {
+ global.github.rest.pulls.requestReviewers.mockRejectedValue(new Error("API error"));
+
+ const { main } = require("./create_pull_request.cjs");
+ const handler = await main({ reviewers: ["user1"], allow_empty: true });
+
+ const result = await handler({ title: "Test PR", body: "Test body" }, {});
+
+ expect(result.success).toBe(true);
+ expect(global.core.warning).toHaveBeenCalledWith(expect.stringContaining("Failed to request reviewers"));
+ });
+
+ it("should accept reviewers as a comma-separated string", async () => {
+ const { main } = require("./create_pull_request.cjs");
+ const handler = await main({ reviewers: "user1,user2", allow_empty: true });
+
+ const result = await handler({ title: "Test PR", body: "Test body" }, {});
+
+ expect(result.success).toBe(true);
+ expect(global.github.rest.pulls.requestReviewers).toHaveBeenCalledWith(expect.objectContaining({ reviewers: ["user1", "user2"] }));
+ });
+});
diff --git a/actions/setup/js/determine_automatic_lockdown.cjs b/actions/setup/js/determine_automatic_lockdown.cjs
index 1a823331bdd..0af3452b3de 100644
--- a/actions/setup/js/determine_automatic_lockdown.cjs
+++ b/actions/setup/js/determine_automatic_lockdown.cjs
@@ -2,21 +2,23 @@
///
/**
- * Determines automatic lockdown mode for GitHub MCP server based on repository visibility
- * and custom token availability.
+ * Determines automatic guard policy for GitHub MCP server based on repository visibility.
*
- * Lockdown mode is automatically enabled for public repositories when ANY custom GitHub token
- * is configured (GH_AW_GITHUB_TOKEN, GH_AW_GITHUB_MCP_SERVER_TOKEN, or custom github-token).
- * This prevents unauthorized access to private repositories that the token may have access to.
+ * This step always sets `min_integrity` and `repos` outputs so that the GitHub MCP
+ * `guard-policies` block is never populated with empty values:
*
- * For public repositories WITHOUT custom tokens, lockdown mode is disabled (false) as
- * the default GITHUB_TOKEN is already scoped to the current repository.
+ * - Public repositories: defaults to `min_integrity=approved`, `repos=all`
+ * - Private/internal repositories: defaults to `min_integrity=none`, `repos=all`
*
- * For private repositories, lockdown mode is not necessary (false) as there is no risk
- * of exposing private repository access.
+ * Whether a field is "already configured" is determined by the environment variables
+ * GH_AW_GITHUB_MIN_INTEGRITY and GH_AW_GITHUB_REPOS, which are set at compile time
+ * from the workflow's tools.github guard policy configuration. Pre-configured values
+ * are never overridden.
*
* Note: This step is NOT generated when tools.github.app is configured. GitHub App tokens
- * are already scoped to specific repositories, so automatic lockdown detection is unnecessary.
+ * are already scoped to specific repositories, so automatic guard policy detection is
+ * unnecessary. It is also NOT generated when both repos and min-integrity are explicitly
+ * configured in the workflow.
*
* @param {any} github - GitHub API client
* @param {any} context - GitHub context
@@ -25,7 +27,7 @@
*/
async function determineAutomaticLockdown(github, context, core) {
try {
- core.info("Determining automatic lockdown mode for GitHub MCP server");
+ core.info("Determining automatic guard policy for GitHub MCP server");
const { owner, repo } = context.repo;
core.info(`Checking repository: ${owner}/${repo}`);
@@ -42,42 +44,69 @@ async function determineAutomaticLockdown(github, context, core) {
core.info(`Repository visibility: ${visibility}`);
core.info(`Repository is private: ${isPrivate}`);
- // Check if any custom GitHub token is configured
- const hasGhAwToken = !!process.env.GH_AW_GITHUB_TOKEN;
- const hasGhAwMcpToken = !!process.env.GH_AW_GITHUB_MCP_SERVER_TOKEN;
- const hasCustomToken = !!process.env.CUSTOM_GITHUB_TOKEN;
- const hasAnyCustomToken = hasGhAwToken || hasGhAwMcpToken || hasCustomToken;
+ core.setOutput("visibility", visibility);
- core.info(`GH_AW_GITHUB_TOKEN configured: ${hasGhAwToken}`);
- core.info(`GH_AW_GITHUB_MCP_SERVER_TOKEN configured: ${hasGhAwMcpToken}`);
- core.info(`Custom github-token configured: ${hasCustomToken}`);
- core.info(`Any custom token configured: ${hasAnyCustomToken}`);
+ // Check whether guard policy fields are already configured at compile time
+ const configuredMinIntegrity = process.env.GH_AW_GITHUB_MIN_INTEGRITY || "";
+ const configuredRepos = process.env.GH_AW_GITHUB_REPOS || "";
- // Set lockdown based on visibility AND custom token availability
- // Public repos with any custom token should have lockdown enabled to prevent token from accessing private repos
- // Public repos without custom tokens use default GITHUB_TOKEN (already scoped), so lockdown is not needed
- const shouldLockdown = !isPrivate && hasAnyCustomToken;
+ core.info(`Configured min-integrity: ${configuredMinIntegrity || "(not set)"}`);
+ core.info(`Configured repos: ${configuredRepos || "(not set)"}`);
- core.info(`Automatic lockdown mode determined: ${shouldLockdown}`);
- core.setOutput("lockdown", shouldLockdown.toString());
- core.setOutput("visibility", visibility);
+ // Private/internal repos default to min_integrity=none; public repos to approved.
+ // Either way, always emit outputs so guard-policies values are never empty.
+ const defaultMinIntegrity = isPrivate ? "none" : "approved";
+ const defaultRepos = "all";
+
+ // Set min_integrity if not already configured
+ const resolvedMinIntegrity = configuredMinIntegrity || defaultMinIntegrity;
+ if (!configuredMinIntegrity) {
+ core.info(`min-integrity not configured — automatically setting to '${defaultMinIntegrity}' for ${visibility} repository`);
+ core.setOutput("min_integrity", defaultMinIntegrity);
+ } else {
+ core.info(`min-integrity already configured as '${configuredMinIntegrity}' — not overriding`);
+ core.setOutput("min_integrity", configuredMinIntegrity);
+ }
- if (shouldLockdown) {
- core.info("Automatic lockdown mode enabled for public repository with custom GitHub token");
- core.warning("GitHub MCP lockdown mode enabled for public repository. " + "This prevents the GitHub token from accessing private repositories.");
- } else if (!isPrivate && !hasAnyCustomToken) {
- core.info("Automatic lockdown mode disabled for public repository (no custom tokens configured)");
- core.info("To enable lockdown mode for enhanced security, configure GH_AW_GITHUB_TOKEN as a repository secret and set 'lockdown: true' in your workflow.");
+ // Set repos if not already configured
+ const resolvedRepos = configuredRepos || defaultRepos;
+ if (!configuredRepos) {
+ core.info(`repos not configured — automatically setting to '${defaultRepos}' for ${visibility} repository`);
+ core.setOutput("repos", defaultRepos);
} else {
- core.info("Automatic lockdown mode disabled for private/internal repository");
+ core.info(`repos already configured as '${configuredRepos}' — not overriding`);
+ core.setOutput("repos", configuredRepos);
}
+
+ if (isPrivate) {
+ core.info("Automatic guard policy determination complete for private/internal repository");
+ } else {
+ core.info("Automatic guard policy determination complete for public repository");
+ core.warning("GitHub MCP guard policy automatically applied for public repository. " + "min-integrity='approved' and repos='all' ensure only approved-integrity content is accessible.");
+ }
+
+ // Write resolved guard policy values to the step summary
+ const autoLabel = isPrivate ? "automatic (private repo)" : "automatic (public repo)";
+ await core.summary
+ .addHeading("GitHub MCP Guard Policy", 3)
+ .addTable([
+ [
+ { data: "Field", header: true },
+ { data: "Value", header: true },
+ { data: "Source", header: true },
+ ],
+ ["min-integrity", resolvedMinIntegrity, configuredMinIntegrity ? "workflow config" : autoLabel],
+ ["repos", resolvedRepos, configuredRepos ? "workflow config" : autoLabel],
+ ])
+ .write();
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
- core.error(`Failed to determine automatic lockdown mode: ${errorMessage}`);
- // Default to lockdown mode for safety
- core.setOutput("lockdown", "true");
+ core.error(`Failed to determine automatic guard policy: ${errorMessage}`);
+ // Default to safe guard policy for public repos on error
+ core.setOutput("min_integrity", "approved");
+ core.setOutput("repos", "all");
core.setOutput("visibility", "unknown");
- core.warning("Failed to determine repository visibility. Defaulting to lockdown mode for security.");
+ core.warning("Failed to determine repository visibility. Defaulting to guard policy min-integrity='approved', repos='all' for security.");
}
}
diff --git a/actions/setup/js/determine_automatic_lockdown.test.cjs b/actions/setup/js/determine_automatic_lockdown.test.cjs
index 7eb6fe2cca5..e34e0f17cee 100644
--- a/actions/setup/js/determine_automatic_lockdown.test.cjs
+++ b/actions/setup/js/determine_automatic_lockdown.test.cjs
@@ -32,20 +32,25 @@ describe("determine_automatic_lockdown", () => {
warning: vi.fn(),
error: vi.fn(),
setOutput: vi.fn(),
+ summary: {
+ addHeading: vi.fn().mockReturnThis(),
+ addTable: vi.fn().mockReturnThis(),
+ write: vi.fn().mockResolvedValue(undefined),
+ },
};
// Reset process.env
delete process.env.GH_AW_GITHUB_TOKEN;
delete process.env.GH_AW_GITHUB_MCP_SERVER_TOKEN;
delete process.env.CUSTOM_GITHUB_TOKEN;
+ delete process.env.GH_AW_GITHUB_MIN_INTEGRITY;
+ delete process.env.GH_AW_GITHUB_REPOS;
// Import the module
determineAutomaticLockdown = (await import("./determine_automatic_lockdown.cjs")).default;
});
- it("should set lockdown to true for public repository with GH_AW_GITHUB_TOKEN", async () => {
- process.env.GH_AW_GITHUB_TOKEN = "ghp_test_token";
-
+ it("should set min_integrity=approved and repos=all for public repository (no guard policy configured)", async () => {
mockGithub.rest.repos.get.mockResolvedValue({
data: {
private: false,
@@ -59,15 +64,15 @@ describe("determine_automatic_lockdown", () => {
owner: "test-owner",
repo: "test-repo",
});
- expect(mockCore.info).toHaveBeenCalledWith("GH_AW_GITHUB_TOKEN configured: true");
- expect(mockCore.info).toHaveBeenCalledWith("Any custom token configured: true");
- expect(mockCore.setOutput).toHaveBeenCalledWith("lockdown", "true");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("min_integrity", "approved");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "all");
expect(mockCore.setOutput).toHaveBeenCalledWith("visibility", "public");
- expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("GitHub MCP lockdown mode enabled"));
+ expect(mockCore.setOutput).not.toHaveBeenCalledWith("lockdown", expect.anything());
+ expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("guard policy automatically applied"));
});
- it("should set lockdown to true for public repository with GH_AW_GITHUB_MCP_SERVER_TOKEN", async () => {
- process.env.GH_AW_GITHUB_MCP_SERVER_TOKEN = "ghp_mcp_token";
+ it("should not override min_integrity when already configured", async () => {
+ process.env.GH_AW_GITHUB_MIN_INTEGRITY = "merged";
mockGithub.rest.repos.get.mockResolvedValue({
data: {
@@ -78,16 +83,14 @@ describe("determine_automatic_lockdown", () => {
await determineAutomaticLockdown(mockGithub, mockContext, mockCore);
- expect(mockCore.info).toHaveBeenCalledWith("GH_AW_GITHUB_TOKEN configured: false");
- expect(mockCore.info).toHaveBeenCalledWith("GH_AW_GITHUB_MCP_SERVER_TOKEN configured: true");
- expect(mockCore.info).toHaveBeenCalledWith("Any custom token configured: true");
- expect(mockCore.setOutput).toHaveBeenCalledWith("lockdown", "true");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("min_integrity", "merged");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "all");
expect(mockCore.setOutput).toHaveBeenCalledWith("visibility", "public");
- expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("GitHub MCP lockdown mode enabled"));
+ expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("min-integrity already configured as 'merged'"));
});
- it("should set lockdown to true for public repository with custom github-token", async () => {
- process.env.CUSTOM_GITHUB_TOKEN = "ghp_custom_token";
+ it("should not override repos when already configured", async () => {
+ process.env.GH_AW_GITHUB_REPOS = "public";
mockGithub.rest.repos.get.mockResolvedValue({
data: {
@@ -98,41 +101,12 @@ describe("determine_automatic_lockdown", () => {
await determineAutomaticLockdown(mockGithub, mockContext, mockCore);
- expect(mockCore.info).toHaveBeenCalledWith("GH_AW_GITHUB_TOKEN configured: false");
- expect(mockCore.info).toHaveBeenCalledWith("GH_AW_GITHUB_MCP_SERVER_TOKEN configured: false");
- expect(mockCore.info).toHaveBeenCalledWith("Custom github-token configured: true");
- expect(mockCore.info).toHaveBeenCalledWith("Any custom token configured: true");
- expect(mockCore.setOutput).toHaveBeenCalledWith("lockdown", "true");
- expect(mockCore.setOutput).toHaveBeenCalledWith("visibility", "public");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("min_integrity", "approved");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "public");
+ expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("repos already configured as 'public'"));
});
- it("should set lockdown to false for public repository without any custom tokens", async () => {
- // No custom tokens set
-
- mockGithub.rest.repos.get.mockResolvedValue({
- data: {
- private: false,
- visibility: "public",
- },
- });
-
- await determineAutomaticLockdown(mockGithub, mockContext, mockCore);
-
- expect(mockGithub.rest.repos.get).toHaveBeenCalledWith({
- owner: "test-owner",
- repo: "test-repo",
- });
- expect(mockCore.info).toHaveBeenCalledWith("GH_AW_GITHUB_TOKEN configured: false");
- expect(mockCore.info).toHaveBeenCalledWith("GH_AW_GITHUB_MCP_SERVER_TOKEN configured: false");
- expect(mockCore.info).toHaveBeenCalledWith("Custom github-token configured: false");
- expect(mockCore.info).toHaveBeenCalledWith("Any custom token configured: false");
- expect(mockCore.setOutput).toHaveBeenCalledWith("lockdown", "false");
- expect(mockCore.setOutput).toHaveBeenCalledWith("visibility", "public");
- expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("Automatic lockdown mode disabled for public repository (no custom tokens configured)"));
- expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("To enable lockdown mode for enhanced security"));
- });
-
- it("should set lockdown to false for private repository", async () => {
+ it("should set min_integrity=none and repos=all for private repository (no guard policy configured)", async () => {
mockGithub.rest.repos.get.mockResolvedValue({
data: {
private: true,
@@ -146,12 +120,14 @@ describe("determine_automatic_lockdown", () => {
owner: "test-owner",
repo: "test-repo",
});
- expect(mockCore.setOutput).toHaveBeenCalledWith("lockdown", "false");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("min_integrity", "none");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "all");
expect(mockCore.setOutput).toHaveBeenCalledWith("visibility", "private");
+ expect(mockCore.setOutput).not.toHaveBeenCalledWith("lockdown", expect.anything());
expect(mockCore.warning).not.toHaveBeenCalled();
});
- it("should set lockdown to false for internal repository", async () => {
+ it("should set min_integrity=none and repos=all for internal repository (no guard policy configured)", async () => {
mockGithub.rest.repos.get.mockResolvedValue({
data: {
private: true,
@@ -161,24 +137,26 @@ describe("determine_automatic_lockdown", () => {
await determineAutomaticLockdown(mockGithub, mockContext, mockCore);
- expect(mockCore.setOutput).toHaveBeenCalledWith("lockdown", "false");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("min_integrity", "none");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "all");
expect(mockCore.setOutput).toHaveBeenCalledWith("visibility", "internal");
});
- it("should handle API failure and default to lockdown mode", async () => {
+ it("should handle API failure and default to safe guard policy", async () => {
const error = new Error("API request failed");
mockGithub.rest.repos.get.mockRejectedValue(error);
await determineAutomaticLockdown(mockGithub, mockContext, mockCore);
- expect(mockCore.error).toHaveBeenCalledWith("Failed to determine automatic lockdown mode: API request failed");
- expect(mockCore.setOutput).toHaveBeenCalledWith("lockdown", "true");
+ expect(mockCore.error).toHaveBeenCalledWith("Failed to determine automatic guard policy: API request failed");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("min_integrity", "approved");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "all");
expect(mockCore.setOutput).toHaveBeenCalledWith("visibility", "unknown");
+ expect(mockCore.setOutput).not.toHaveBeenCalledWith("lockdown", expect.anything());
expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Failed to determine repository visibility"));
});
it("should infer visibility from private field when visibility field is missing", async () => {
- // Public repo without GH_AW_GITHUB_TOKEN
mockGithub.rest.repos.get.mockResolvedValue({
data: {
private: false,
@@ -188,13 +166,32 @@ describe("determine_automatic_lockdown", () => {
await determineAutomaticLockdown(mockGithub, mockContext, mockCore);
- expect(mockCore.setOutput).toHaveBeenCalledWith("lockdown", "false");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("min_integrity", "approved");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "all");
expect(mockCore.setOutput).toHaveBeenCalledWith("visibility", "public");
});
- it("should log appropriate info messages for public repo with token", async () => {
- process.env.GH_AW_GITHUB_TOKEN = "ghp_test_token";
+ it("should not override either guard policy field when both are already configured", async () => {
+ process.env.GH_AW_GITHUB_MIN_INTEGRITY = "approved";
+ process.env.GH_AW_GITHUB_REPOS = "public";
+
+ mockGithub.rest.repos.get.mockResolvedValue({
+ data: {
+ private: false,
+ visibility: "public",
+ },
+ });
+
+ await determineAutomaticLockdown(mockGithub, mockContext, mockCore);
+
+ expect(mockCore.setOutput).toHaveBeenCalledWith("min_integrity", "approved");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "public");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("visibility", "public");
+ expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("min-integrity already configured as 'approved'"));
+ expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("repos already configured as 'public'"));
+ });
+ it("should log appropriate info messages for public repo", async () => {
mockGithub.rest.repos.get.mockResolvedValue({
data: {
private: false,
@@ -204,12 +201,80 @@ describe("determine_automatic_lockdown", () => {
await determineAutomaticLockdown(mockGithub, mockContext, mockCore);
- expect(mockCore.info).toHaveBeenCalledWith("Determining automatic lockdown mode for GitHub MCP server");
+ expect(mockCore.info).toHaveBeenCalledWith("Determining automatic guard policy for GitHub MCP server");
expect(mockCore.info).toHaveBeenCalledWith("Checking repository: test-owner/test-repo");
expect(mockCore.info).toHaveBeenCalledWith("Repository visibility: public");
expect(mockCore.info).toHaveBeenCalledWith("Repository is private: false");
- expect(mockCore.info).toHaveBeenCalledWith("GH_AW_GITHUB_TOKEN configured: true");
- expect(mockCore.info).toHaveBeenCalledWith("Automatic lockdown mode determined: true");
- expect(mockCore.info).toHaveBeenCalledWith("Automatic lockdown mode enabled for public repository with custom GitHub token");
+ expect(mockCore.info).toHaveBeenCalledWith("Automatic guard policy determination complete for public repository");
+ });
+
+ it("should write resolved guard policy values to step summary for public repository", async () => {
+ mockGithub.rest.repos.get.mockResolvedValue({
+ data: {
+ private: false,
+ visibility: "public",
+ },
+ });
+
+ await determineAutomaticLockdown(mockGithub, mockContext, mockCore);
+
+ expect(mockCore.summary.addHeading).toHaveBeenCalledWith("GitHub MCP Guard Policy", 3);
+ expect(mockCore.summary.addTable).toHaveBeenCalledWith([
+ [
+ { data: "Field", header: true },
+ { data: "Value", header: true },
+ { data: "Source", header: true },
+ ],
+ ["min-integrity", "approved", "automatic (public repo)"],
+ ["repos", "all", "automatic (public repo)"],
+ ]);
+ expect(mockCore.summary.write).toHaveBeenCalled();
+ });
+
+ it("should show workflow config as source in summary when guard policy fields are pre-configured", async () => {
+ process.env.GH_AW_GITHUB_MIN_INTEGRITY = "merged";
+ process.env.GH_AW_GITHUB_REPOS = "public";
+
+ mockGithub.rest.repos.get.mockResolvedValue({
+ data: {
+ private: false,
+ visibility: "public",
+ },
+ });
+
+ await determineAutomaticLockdown(mockGithub, mockContext, mockCore);
+
+ expect(mockCore.summary.addTable).toHaveBeenCalledWith([
+ [
+ { data: "Field", header: true },
+ { data: "Value", header: true },
+ { data: "Source", header: true },
+ ],
+ ["min-integrity", "merged", "workflow config"],
+ ["repos", "public", "workflow config"],
+ ]);
+ expect(mockCore.summary.write).toHaveBeenCalled();
+ });
+
+ it("should write resolved guard policy values to step summary for private repository", async () => {
+ mockGithub.rest.repos.get.mockResolvedValue({
+ data: {
+ private: true,
+ visibility: "private",
+ },
+ });
+
+ await determineAutomaticLockdown(mockGithub, mockContext, mockCore);
+
+ expect(mockCore.summary.addTable).toHaveBeenCalledWith([
+ [
+ { data: "Field", header: true },
+ { data: "Value", header: true },
+ { data: "Source", header: true },
+ ],
+ ["min-integrity", "none", "automatic (private repo)"],
+ ["repos", "all", "automatic (private repo)"],
+ ]);
+ expect(mockCore.summary.write).toHaveBeenCalled();
});
});
diff --git a/actions/setup/js/generate_aw_info.cjs b/actions/setup/js/generate_aw_info.cjs
index 6be4f2fa87b..961a59b645b 100644
--- a/actions/setup/js/generate_aw_info.cjs
+++ b/actions/setup/js/generate_aw_info.cjs
@@ -86,6 +86,12 @@ async function main(core, ctx) {
awInfo.cli_version = cliVersion;
}
+ // Include apm_version only when APM dependencies are configured
+ const apmVersion = process.env.GH_AW_INFO_APM_VERSION;
+ if (apmVersion) {
+ awInfo.apm_version = apmVersion;
+ }
+
// Write to /tmp/gh-aw directory to avoid inclusion in PR
fs.mkdirSync(TMP_GH_AW_PATH, { recursive: true });
const tmpPath = TMP_GH_AW_PATH + "/aw_info.json";
diff --git a/actions/setup/js/generate_safe_outputs_tools.cjs b/actions/setup/js/generate_safe_outputs_tools.cjs
new file mode 100644
index 00000000000..305979e5407
--- /dev/null
+++ b/actions/setup/js/generate_safe_outputs_tools.cjs
@@ -0,0 +1,102 @@
+// @ts-check
+"use strict";
+
+/**
+ * generate_safe_outputs_tools.cjs
+ *
+ * This script generates the safe outputs tools.json at runtime by:
+ * 1. Loading the full safe_outputs_tools.json from the actions folder
+ * 2. Filtering tools based on config.json (which tools are enabled)
+ * 3. Applying description suffixes and repo parameters from tools_meta.json
+ * 4. Appending dynamic tools (dispatch_workflow, call_workflow, custom jobs) from tools_meta.json
+ * 5. Writing the result to the output tools.json path
+ *
+ * Environment variables:
+ * GH_AW_SAFE_OUTPUTS_TOOLS_SOURCE_PATH - Path to the source safe_outputs_tools.json
+ * Default: /opt/gh-aw/actions/safe_outputs_tools.json
+ * GH_AW_SAFE_OUTPUTS_CONFIG_PATH - Path to config.json (used to determine enabled tools)
+ * Default: /opt/gh-aw/safeoutputs/config.json
+ * GH_AW_SAFE_OUTPUTS_TOOLS_META_PATH - Path to tools_meta.json (descriptions, repo params, dynamic tools)
+ * Default: /opt/gh-aw/safeoutputs/tools_meta.json
+ * GH_AW_SAFE_OUTPUTS_TOOLS_PATH - Output path for the generated tools.json
+ * Default: /opt/gh-aw/safeoutputs/tools.json
+ */
+
+const fs = require("fs");
+const path = require("path");
+
+const toolsSourcePath = process.env.GH_AW_SAFE_OUTPUTS_TOOLS_SOURCE_PATH || "/opt/gh-aw/actions/safe_outputs_tools.json";
+const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/opt/gh-aw/safeoutputs/config.json";
+const toolsMetaPath = process.env.GH_AW_SAFE_OUTPUTS_TOOLS_META_PATH || path.join(path.dirname(configPath), "tools_meta.json");
+const outputPath = process.env.GH_AW_SAFE_OUTPUTS_TOOLS_PATH || "/opt/gh-aw/safeoutputs/tools.json";
+
+// Load all source tools from the actions folder
+if (!fs.existsSync(toolsSourcePath)) {
+ console.error(`Error: Source tools file not found at: ${toolsSourcePath}`);
+ process.exit(1);
+}
+/** @type {Array<{name: string, description: string, inputSchema?: {properties?: Record}}>} */
+const allTools = JSON.parse(fs.readFileSync(toolsSourcePath, "utf8"));
+
+// Load config to determine which tools are enabled
+if (!fs.existsSync(configPath)) {
+ console.error(`Error: Config file not found at: ${configPath}`);
+ process.exit(1);
+}
+/** @type {Record} */
+const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
+
+// Load tools meta (description suffixes, repo params, dynamic tools)
+/** @type {{description_suffixes?: Record, repo_params?: Record, dynamic_tools?: Array}} */
+let toolsMeta = { description_suffixes: {}, repo_params: {}, dynamic_tools: [] };
+if (fs.existsSync(toolsMetaPath)) {
+ toolsMeta = JSON.parse(fs.readFileSync(toolsMetaPath, "utf8"));
+}
+
+// Build set of source tool names (predefined/static tools only)
+const sourceToolNames = new Set(allTools.map(t => t.name));
+
+// Determine enabled tools: config keys that match source tool names
+// This filters out non-tool config entries like dispatch_workflow, call_workflow,
+// mentions, max_bot_mentions, etc.
+const enabledToolNames = new Set(Object.keys(config).filter(k => sourceToolNames.has(k)));
+
+// Filter predefined tools to those enabled in config and apply enhancements
+const filteredTools = allTools
+ .filter(tool => enabledToolNames.has(tool.name))
+ .map(tool => {
+ // Deep copy to avoid modifying the original
+ const enhancedTool = JSON.parse(JSON.stringify(tool));
+
+ // Apply description suffix if available (e.g., " CONSTRAINTS: Maximum 5 issues.")
+ const descSuffix = toolsMeta.description_suffixes?.[tool.name];
+ if (descSuffix) {
+ enhancedTool.description = (enhancedTool.description || "") + descSuffix;
+ }
+
+ // Add repo parameter to inputSchema if configured
+ const repoParam = toolsMeta.repo_params?.[tool.name];
+ if (repoParam) {
+ if (!enhancedTool.inputSchema) {
+ enhancedTool.inputSchema = { type: "object", properties: {} };
+ }
+ if (!enhancedTool.inputSchema.properties) {
+ enhancedTool.inputSchema.properties = {};
+ }
+ enhancedTool.inputSchema.properties.repo = repoParam;
+ }
+
+ return enhancedTool;
+ });
+
+// Append dynamic tools (custom jobs, dispatch_workflow, call_workflow)
+const dynamicTools = Array.isArray(toolsMeta.dynamic_tools) ? toolsMeta.dynamic_tools : [];
+const allFilteredTools = [...filteredTools, ...dynamicTools];
+
+// Write the result to the output path
+fs.writeFileSync(outputPath, JSON.stringify(allFilteredTools, null, 2));
+
+const debugEnabled = process.env.DEBUG === "*" || (process.env.DEBUG || "").includes("safe_outputs");
+if (debugEnabled) {
+ console.log(`Generated tools.json with ${allFilteredTools.length} tools (${filteredTools.length} static + ${dynamicTools.length} dynamic)`);
+}
diff --git a/actions/setup/js/generate_safe_outputs_tools.test.cjs b/actions/setup/js/generate_safe_outputs_tools.test.cjs
new file mode 100644
index 00000000000..9b09093a7b0
--- /dev/null
+++ b/actions/setup/js/generate_safe_outputs_tools.test.cjs
@@ -0,0 +1,254 @@
+// @ts-check
+import { describe, it, expect, beforeEach, afterEach } from "vitest";
+import fs from "fs";
+import path from "path";
+import { execSync } from "child_process";
+
+const scriptPath = path.join(__dirname, "generate_safe_outputs_tools.cjs");
+
+describe("generate_safe_outputs_tools", () => {
+ /** @type {string} */
+ let testDir;
+ /** @type {string} */
+ let toolsSourcePath;
+ /** @type {string} */
+ let configPath;
+ /** @type {string} */
+ let toolsMetaPath;
+ /** @type {string} */
+ let outputPath;
+
+ const sampleSourceTools = [
+ {
+ name: "create_issue",
+ description: "Creates a GitHub issue.",
+ inputSchema: {
+ type: "object",
+ properties: {
+ title: { type: "string", description: "Issue title" },
+ body: { type: "string", description: "Issue body" },
+ },
+ required: ["title"],
+ },
+ },
+ {
+ name: "add_comment",
+ description: "Adds a comment.",
+ inputSchema: {
+ type: "object",
+ properties: {
+ body: { type: "string", description: "Comment body" },
+ },
+ required: ["body"],
+ },
+ },
+ {
+ name: "missing_tool",
+ description: "Reports a missing tool.",
+ inputSchema: { type: "object", properties: {} },
+ },
+ ];
+
+ beforeEach(() => {
+ const testId = Math.random().toString(36).substring(7);
+ testDir = `/tmp/test-generate-tools-${testId}`;
+ fs.mkdirSync(testDir, { recursive: true });
+
+ toolsSourcePath = path.join(testDir, "safe_outputs_tools.json");
+ configPath = path.join(testDir, "config.json");
+ toolsMetaPath = path.join(testDir, "tools_meta.json");
+ outputPath = path.join(testDir, "tools.json");
+
+ // Write source tools
+ fs.writeFileSync(toolsSourcePath, JSON.stringify(sampleSourceTools));
+ });
+
+ afterEach(() => {
+ fs.rmSync(testDir, { recursive: true, force: true });
+ });
+
+ /**
+ * Run the generate script with the test env vars.
+ * @param {Record} [extraEnv] Additional env vars to set.
+ * @returns {string} stdout output of the script.
+ */
+ function runScript(extraEnv = {}) {
+ const env = {
+ ...process.env,
+ GH_AW_SAFE_OUTPUTS_TOOLS_SOURCE_PATH: toolsSourcePath,
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: configPath,
+ GH_AW_SAFE_OUTPUTS_TOOLS_META_PATH: toolsMetaPath,
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: outputPath,
+ ...extraEnv,
+ };
+ return execSync(`node ${scriptPath}`, { env, encoding: "utf8" });
+ }
+
+ it("filters tools based on config keys", () => {
+ // Only create_issue and add_comment are enabled
+ fs.writeFileSync(configPath, JSON.stringify({ create_issue: { max: 5 }, add_comment: { max: 10 } }));
+ fs.writeFileSync(toolsMetaPath, JSON.stringify({ description_suffixes: {}, repo_params: {}, dynamic_tools: [] }));
+
+ runScript();
+
+ const result = JSON.parse(fs.readFileSync(outputPath, "utf8"));
+ expect(result).toHaveLength(2);
+ expect(result.map((/** @type {{name: string}} */ t) => t.name)).toEqual(expect.arrayContaining(["create_issue", "add_comment"]));
+ // missing_tool should NOT be included since it's not in config
+ expect(result.map((/** @type {{name: string}} */ t) => t.name)).not.toContain("missing_tool");
+ });
+
+ it("applies description suffix from tools_meta", () => {
+ fs.writeFileSync(configPath, JSON.stringify({ create_issue: { max: 5 } }));
+ fs.writeFileSync(
+ toolsMetaPath,
+ JSON.stringify({
+ description_suffixes: {
+ create_issue: " CONSTRAINTS: Maximum 5 issue(s) can be created.",
+ },
+ repo_params: {},
+ dynamic_tools: [],
+ })
+ );
+
+ runScript();
+
+ const result = JSON.parse(fs.readFileSync(outputPath, "utf8"));
+ const createIssueTool = result.find((/** @type {{name: string}} */ t) => t.name === "create_issue");
+ expect(createIssueTool).toBeDefined();
+ expect(createIssueTool.description).toContain("Creates a GitHub issue.");
+ expect(createIssueTool.description).toContain("CONSTRAINTS: Maximum 5 issue(s) can be created.");
+ });
+
+ it("adds repo parameter when specified in tools_meta", () => {
+ fs.writeFileSync(configPath, JSON.stringify({ create_issue: { max: 5 } }));
+ fs.writeFileSync(
+ toolsMetaPath,
+ JSON.stringify({
+ description_suffixes: {},
+ repo_params: {
+ create_issue: {
+ type: "string",
+ description: "Target repository in 'owner/repo' format.",
+ },
+ },
+ dynamic_tools: [],
+ })
+ );
+
+ runScript();
+
+ const result = JSON.parse(fs.readFileSync(outputPath, "utf8"));
+ const createIssueTool = result.find((/** @type {{name: string}} */ t) => t.name === "create_issue");
+ expect(createIssueTool).toBeDefined();
+ expect(createIssueTool.inputSchema.properties.repo).toBeDefined();
+ expect(createIssueTool.inputSchema.properties.repo.type).toBe("string");
+ });
+
+ it("appends dynamic tools from tools_meta", () => {
+ fs.writeFileSync(configPath, JSON.stringify({ create_issue: { max: 1 } }));
+ fs.writeFileSync(
+ toolsMetaPath,
+ JSON.stringify({
+ description_suffixes: {},
+ repo_params: {},
+ dynamic_tools: [
+ {
+ name: "dispatch_deploy_workflow",
+ description: "Dispatches the deploy workflow.",
+ inputSchema: { type: "object", properties: { env: { type: "string" } } },
+ _workflow_name: "deploy",
+ },
+ ],
+ })
+ );
+
+ runScript();
+
+ const result = JSON.parse(fs.readFileSync(outputPath, "utf8"));
+ expect(result).toHaveLength(2);
+ expect(result.map((/** @type {{name: string}} */ t) => t.name)).toContain("dispatch_deploy_workflow");
+ const dynamicTool = result.find((/** @type {{name: string, _workflow_name?: string}} */ t) => t._workflow_name === "deploy");
+ expect(dynamicTool).toBeDefined();
+ });
+
+ it("handles empty config with no enabled tools", () => {
+ fs.writeFileSync(configPath, JSON.stringify({}));
+ fs.writeFileSync(toolsMetaPath, JSON.stringify({ description_suffixes: {}, repo_params: {}, dynamic_tools: [] }));
+
+ runScript();
+
+ const result = JSON.parse(fs.readFileSync(outputPath, "utf8"));
+ expect(result).toHaveLength(0);
+ });
+
+ it("ignores non-tool config keys when filtering", () => {
+ // dispatch_workflow and max_bot_mentions are not tool names in source file
+ fs.writeFileSync(
+ configPath,
+ JSON.stringify({
+ create_issue: { max: 1 },
+ dispatch_workflow: { workflows: ["deploy"] },
+ max_bot_mentions: 5,
+ })
+ );
+ fs.writeFileSync(toolsMetaPath, JSON.stringify({ description_suffixes: {}, repo_params: {}, dynamic_tools: [] }));
+
+ runScript();
+
+ const result = JSON.parse(fs.readFileSync(outputPath, "utf8"));
+ // Only create_issue should be in filtered static tools
+ expect(result.map((/** @type {{name: string}} */ t) => t.name)).not.toContain("dispatch_workflow");
+ expect(result.map((/** @type {{name: string}} */ t) => t.name)).not.toContain("max_bot_mentions");
+ expect(result.map((/** @type {{name: string}} */ t) => t.name)).toContain("create_issue");
+ });
+
+ it("does not modify source tools in memory (deep copy)", () => {
+ fs.writeFileSync(configPath, JSON.stringify({ create_issue: { max: 5 } }));
+ fs.writeFileSync(
+ toolsMetaPath,
+ JSON.stringify({
+ description_suffixes: { create_issue: " CONSTRAINTS: Maximum 5 issue(s)." },
+ repo_params: {
+ create_issue: { type: "string", description: "Target repo" },
+ },
+ dynamic_tools: [],
+ })
+ );
+
+ // Run twice to ensure source tools are not modified between runs
+ runScript();
+ const result1 = JSON.parse(fs.readFileSync(outputPath, "utf8"));
+ runScript();
+ const result2 = JSON.parse(fs.readFileSync(outputPath, "utf8"));
+
+ expect(result1[0].description).toEqual(result2[0].description);
+ expect(result1[0].inputSchema.properties.repo).toEqual(result2[0].inputSchema.properties.repo);
+ });
+
+ it("exits with error when source tools file is missing", () => {
+ fs.writeFileSync(configPath, JSON.stringify({ create_issue: {} }));
+ fs.writeFileSync(toolsMetaPath, JSON.stringify({ description_suffixes: {}, repo_params: {}, dynamic_tools: [] }));
+
+ expect(() => runScript({ GH_AW_SAFE_OUTPUTS_TOOLS_SOURCE_PATH: "/nonexistent/path.json" })).toThrow();
+ });
+
+ it("exits with error when config file is missing", () => {
+ fs.writeFileSync(toolsMetaPath, JSON.stringify({ description_suffixes: {}, repo_params: {}, dynamic_tools: [] }));
+
+ expect(() => runScript({ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: "/nonexistent/config.json" })).toThrow();
+ });
+
+ it("works when tools_meta file is missing (graceful fallback)", () => {
+ fs.writeFileSync(configPath, JSON.stringify({ create_issue: { max: 1 } }));
+ // No tools_meta.json - should still work with fallback to empty meta
+
+ runScript({ GH_AW_SAFE_OUTPUTS_TOOLS_META_PATH: "/nonexistent/tools_meta.json" });
+
+ const result = JSON.parse(fs.readFileSync(outputPath, "utf8"));
+ expect(result).toHaveLength(1);
+ expect(result[0].name).toBe("create_issue");
+ // Description should be unchanged (no suffix applied)
+ expect(result[0].description).toBe("Creates a GitHub issue.");
+ });
+});
diff --git a/actions/setup/js/remove_trigger_label.cjs b/actions/setup/js/remove_trigger_label.cjs
new file mode 100644
index 00000000000..102342778a8
--- /dev/null
+++ b/actions/setup/js/remove_trigger_label.cjs
@@ -0,0 +1,139 @@
+// @ts-check
+///
+
+const { ERR_API, ERR_CONFIG } = require("./error_codes.cjs");
+
+/**
+ * Remove the label that triggered this workflow from the issue, pull request, or discussion.
+ * This allows the same label to be applied again later to re-trigger the workflow.
+ *
+ * Supported events: issues (labeled), pull_request (labeled), discussion (labeled).
+ * For workflow_dispatch, the step emits an empty label_name output and exits without error.
+ */
+async function main() {
+ const labelNamesJSON = process.env.GH_AW_LABEL_NAMES;
+
+ const { getErrorMessage } = require("./error_helpers.cjs");
+
+ if (!labelNamesJSON) {
+ core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_LABEL_NAMES not specified.`);
+ return;
+ }
+
+ let labelNames = [];
+ try {
+ labelNames = JSON.parse(labelNamesJSON);
+ if (!Array.isArray(labelNames)) {
+ core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_LABEL_NAMES must be a JSON array.`);
+ return;
+ }
+ } catch (error) {
+ core.setFailed(`${ERR_CONFIG}: Configuration error: Failed to parse GH_AW_LABEL_NAMES: ${getErrorMessage(error)}`);
+ return;
+ }
+
+ const eventName = context.eventName;
+
+ // For workflow_dispatch and other non-labeled events, nothing to remove.
+ if (eventName === "workflow_dispatch") {
+ core.info("Event is workflow_dispatch – skipping label removal.");
+ core.setOutput("label_name", "");
+ return;
+ }
+
+ // Retrieve the label that was added from the event payload.
+ const triggerLabel = context.payload?.label?.name;
+ if (!triggerLabel) {
+ core.info(`Event ${eventName} has no label payload – skipping label removal.`);
+ core.setOutput("label_name", "");
+ return;
+ }
+
+ // Confirm that this label is one of the configured command labels.
+ if (!labelNames.includes(triggerLabel)) {
+ core.info(`Trigger label '${triggerLabel}' is not in the configured label-command list [${labelNames.join(", ")}] – skipping removal.`);
+ core.setOutput("label_name", triggerLabel);
+ return;
+ }
+
+ core.info(`Removing trigger label '${triggerLabel}' (event: ${eventName})`);
+
+ const owner = context.repo?.owner;
+ const repo = context.repo?.repo;
+ if (!owner || !repo) {
+ core.setFailed(`${ERR_CONFIG}: Configuration error: Unable to determine repository owner/name from context.`);
+ return;
+ }
+
+ try {
+ if (eventName === "issues") {
+ const issueNumber = context.payload?.issue?.number;
+ if (!issueNumber) {
+ core.warning("No issue number found in payload – skipping label removal.");
+ core.setOutput("label_name", triggerLabel);
+ return;
+ }
+ await github.rest.issues.removeLabel({
+ owner,
+ repo,
+ issue_number: issueNumber,
+ name: triggerLabel,
+ });
+ core.info(`✓ Removed label '${triggerLabel}' from issue #${issueNumber}`);
+ } else if (eventName === "pull_request") {
+ // Pull requests share the issues API for labels.
+ const prNumber = context.payload?.pull_request?.number;
+ if (!prNumber) {
+ core.warning("No pull request number found in payload – skipping label removal.");
+ core.setOutput("label_name", triggerLabel);
+ return;
+ }
+ await github.rest.issues.removeLabel({
+ owner,
+ repo,
+ issue_number: prNumber,
+ name: triggerLabel,
+ });
+ core.info(`✓ Removed label '${triggerLabel}' from pull request #${prNumber}`);
+ } else if (eventName === "discussion") {
+ // Discussions require the GraphQL API for label management.
+ const discussionNodeId = context.payload?.discussion?.node_id;
+ const labelNodeId = context.payload?.label?.node_id;
+ if (!discussionNodeId || !labelNodeId) {
+ core.warning("No discussion or label node_id found in payload – skipping label removal.");
+ core.setOutput("label_name", triggerLabel);
+ return;
+ }
+ await github.graphql(
+ `
+ mutation RemoveLabelFromDiscussion($labelableId: ID!, $labelIds: [ID!]!) {
+ removeLabelsFromLabelable(input: { labelableId: $labelableId, labelIds: $labelIds }) {
+ clientMutationId
+ }
+ }
+ `,
+ {
+ labelableId: discussionNodeId,
+ labelIds: [labelNodeId],
+ }
+ );
+ core.info(`✓ Removed label '${triggerLabel}' from discussion`);
+ } else {
+ core.info(`Event '${eventName}' does not support label removal – skipping.`);
+ }
+ } catch (/** @type {any} */ error) {
+ // Non-fatal: log a warning but do not fail the step.
+ // A 404 status means the label is no longer present on the item (e.g., another concurrent
+ // workflow run already removed it), which is an expected outcome in multi-workflow setups.
+ const status = error?.status;
+ if (status === 404) {
+ core.info(`Label '${triggerLabel}' is no longer present on the item – already removed by another run.`);
+ } else {
+ core.warning(`${ERR_API}: Failed to remove label '${triggerLabel}': ${getErrorMessage(error)}`);
+ }
+ }
+
+ core.setOutput("label_name", triggerLabel);
+}
+
+module.exports = { main };
diff --git a/actions/setup/js/safe_output_handler_manager.cjs b/actions/setup/js/safe_output_handler_manager.cjs
index 035c9135008..ff09c736d65 100644
--- a/actions/setup/js/safe_output_handler_manager.cjs
+++ b/actions/setup/js/safe_output_handler_manager.cjs
@@ -57,6 +57,7 @@ const HANDLER_MAP = {
create_code_scanning_alert: "./create_code_scanning_alert.cjs",
autofix_code_scanning_alert: "./autofix_code_scanning_alert.cjs",
dispatch_workflow: "./dispatch_workflow.cjs",
+ call_workflow: "./call_workflow.cjs",
create_missing_tool_issue: "./create_missing_tool_issue.cjs",
missing_tool: "./missing_tool.cjs",
create_missing_data_issue: "./create_missing_data_issue.cjs",
diff --git a/actions/setup/js/safe_output_handler_manager.test.cjs b/actions/setup/js/safe_output_handler_manager.test.cjs
index 7da8c48ecaa..699676d3650 100644
--- a/actions/setup/js/safe_output_handler_manager.test.cjs
+++ b/actions/setup/js/safe_output_handler_manager.test.cjs
@@ -1108,4 +1108,36 @@ describe("Safe Output Handler Manager", () => {
expect(global.core.setOutput).toHaveBeenCalledWith("created_issue_url", "https://github.com/owner/repo/issues/7");
});
});
+
+ describe("call_workflow handler registration", () => {
+ it("processes call_workflow messages without no-handler warnings when handler is registered", async () => {
+ const messages = [{ type: "call_workflow", workflow_name: "worker-a" }];
+ const mockHandler = vi.fn().mockResolvedValue({
+ success: true,
+ workflow_name: "worker-a",
+ payload: "{}",
+ });
+ const handlers = new Map([["call_workflow", mockHandler]]);
+
+ const result = await processMessages(handlers, messages);
+
+ expect(result.results).toHaveLength(1);
+ expect(result.results[0].type).toBe("call_workflow");
+ // Handler was invoked, so no "no handler loaded" error
+ expect(result.results[0].error).toBeUndefined();
+ expect(mockHandler).toHaveBeenCalledTimes(1);
+ });
+
+ it("records no-handler error for call_workflow when handler map is missing entry", async () => {
+ const messages = [{ type: "call_workflow", workflow_name: "worker-a" }];
+ // Empty handler map - simulates the bug where call_workflow was not in HANDLER_MAP
+ const handlers = new Map();
+
+ const result = await processMessages(handlers, messages);
+
+ expect(result.results).toHaveLength(1);
+ expect(result.results[0].success).toBe(false);
+ expect(result.results[0].error).toContain("No handler loaded for type 'call_workflow'");
+ });
+ });
});
diff --git a/actions/setup/js/safe_outputs_mcp_server_http.cjs b/actions/setup/js/safe_outputs_mcp_server_http.cjs
index 7abf5841113..2d04b0bd382 100644
--- a/actions/setup/js/safe_outputs_mcp_server_http.cjs
+++ b/actions/setup/js/safe_outputs_mcp_server_http.cjs
@@ -115,6 +115,11 @@ function createMCPServer(options = {}) {
// The _workflow_name should be a non-empty string
const isDispatchWorkflowTool = tool._workflow_name && typeof tool._workflow_name === "string" && tool._workflow_name.length > 0;
+ // Check if this is a call_workflow tool (has _call_workflow_name metadata)
+ // These tools are dynamically generated with workflow-specific names
+ // The _call_workflow_name should be a non-empty string
+ const isCallWorkflowTool = tool._call_workflow_name && typeof tool._call_workflow_name === "string" && tool._call_workflow_name.length > 0;
+
if (isDispatchWorkflowTool) {
logger.debug(`Found dispatch_workflow tool: ${tool.name} (_workflow_name: ${tool._workflow_name})`);
if (!safeOutputsConfig.dispatch_workflow) {
@@ -124,11 +129,20 @@ function createMCPServer(options = {}) {
continue;
}
logger.debug(` dispatch_workflow config exists, registering tool`);
+ } else if (isCallWorkflowTool) {
+ logger.debug(`Found call_workflow tool: ${tool.name} (_call_workflow_name: ${tool._call_workflow_name})`);
+ if (!safeOutputsConfig.call_workflow) {
+ logger.debug(` WARNING: call_workflow config is missing or falsy - tool will NOT be registered`);
+ logger.debug(` Config keys: ${Object.keys(safeOutputsConfig).join(", ")}`);
+ logger.debug(` config.call_workflow value: ${JSON.stringify(safeOutputsConfig.call_workflow)}`);
+ continue;
+ }
+ logger.debug(` call_workflow config exists, registering tool`);
} else {
// Check if regular tool is enabled in configuration
if (!enabledTools.has(tool.name)) {
// Log tool metadata to help diagnose registration issues
- const toolMeta = tool._workflow_name !== undefined ? ` (_workflow_name: ${JSON.stringify(tool._workflow_name)})` : "";
+ const toolMeta = tool._workflow_name !== undefined ? ` (_workflow_name: ${JSON.stringify(tool._workflow_name)})` : tool._call_workflow_name !== undefined ? ` (_call_workflow_name: ${JSON.stringify(tool._call_workflow_name)})` : "";
logger.debug(`Skipping tool ${tool.name}${toolMeta} - not enabled in config (tool has ${Object.keys(tool).length} properties: ${Object.keys(tool).join(", ")})`);
continue;
}
diff --git a/actions/setup/js/safe_outputs_mcp_server_http_call_workflow.test.cjs b/actions/setup/js/safe_outputs_mcp_server_http_call_workflow.test.cjs
new file mode 100644
index 00000000000..a6a2cb5b493
--- /dev/null
+++ b/actions/setup/js/safe_outputs_mcp_server_http_call_workflow.test.cjs
@@ -0,0 +1,525 @@
+// @ts-check
+import { describe, it, expect, beforeEach, afterEach } from "vitest";
+import * as fs from "fs";
+import * as path from "path";
+import * as os from "os";
+const { createMCPServer } = require("./safe_outputs_mcp_server_http.cjs");
+
+/**
+ * Regression tests for call_workflow tool registration in HTTP server.
+ *
+ * These tests verify that the HTTP server correctly registers call_workflow tools
+ * that have _call_workflow_name metadata. Before the fix, these tools fell through
+ * to the generic enabledTools.has(tool.name) check, which always failed because
+ * the config key is "call_workflow" while the tool name is the workflow-specific
+ * name (e.g. "generic_worker").
+ *
+ * Reference: Issue where call_workflow tools were not being registered by the HTTP
+ * server even though the compiler generated them and safe_outputs_tools_loader.cjs
+ * handled them correctly.
+ */
+describe("safe_outputs_mcp_server_http call_workflow registration", () => {
+ /**
+ * Helper that replicates the HTTP server predefined-tool registration logic
+ * so we can test it in isolation.
+ *
+ * @param {Object} tool - Tool definition
+ * @param {Object} config - Safe outputs configuration
+ * @returns {boolean} Whether the tool should be registered
+ */
+ function shouldRegisterTool(tool, config) {
+ const enabledTools = new Set();
+ for (const [toolName, enabled] of Object.entries(config)) {
+ if (enabled) {
+ enabledTools.add(toolName);
+ }
+ }
+
+ const isDispatchWorkflowTool = tool._workflow_name && typeof tool._workflow_name === "string" && tool._workflow_name.length > 0;
+ const isCallWorkflowTool = tool._call_workflow_name && typeof tool._call_workflow_name === "string" && tool._call_workflow_name.length > 0;
+
+ if (isDispatchWorkflowTool) {
+ return !!config.dispatch_workflow;
+ } else if (isCallWorkflowTool) {
+ return !!config.call_workflow;
+ } else {
+ return enabledTools.has(tool.name);
+ }
+ }
+
+ it("should register call_workflow tools when config.call_workflow exists", () => {
+ const tool = {
+ name: "generic_worker",
+ _call_workflow_name: "generic-worker",
+ description: "Call the 'generic-worker' workflow",
+ inputSchema: {
+ type: "object",
+ properties: {
+ task: {
+ type: "string",
+ description: "Task to perform",
+ },
+ },
+ additionalProperties: false,
+ },
+ };
+
+ const config = {
+ call_workflow: {
+ workflows: ["generic-worker"],
+ },
+ missing_tool: {},
+ missing_data: {},
+ noop: { max: 1 },
+ };
+
+ expect(shouldRegisterTool(tool, config)).toBe(true);
+ });
+
+ it("should NOT register call_workflow tools when config.call_workflow is absent", () => {
+ const tool = {
+ name: "generic_worker",
+ _call_workflow_name: "generic-worker",
+ description: "Call the 'generic-worker' workflow",
+ inputSchema: { type: "object", properties: {} },
+ };
+
+ // Config WITHOUT call_workflow
+ const config = {
+ missing_tool: {},
+ missing_data: {},
+ noop: { max: 1 },
+ };
+
+ expect(shouldRegisterTool(tool, config)).toBe(false);
+ });
+
+ it("should register multiple call_workflow tools when config.call_workflow exists", () => {
+ const tools = [
+ {
+ name: "generic_worker",
+ _call_workflow_name: "generic-worker",
+ description: "Call the 'generic-worker' workflow",
+ inputSchema: { type: "object", properties: {} },
+ },
+ {
+ name: "specialist_worker",
+ _call_workflow_name: "specialist-worker",
+ description: "Call the 'specialist-worker' workflow",
+ inputSchema: { type: "object", properties: {} },
+ },
+ ];
+
+ const config = {
+ call_workflow: {
+ workflows: ["generic-worker", "specialist-worker"],
+ },
+ };
+
+ for (const tool of tools) {
+ expect(shouldRegisterTool(tool, config)).toBe(true, `Tool ${tool.name} should be registered`);
+ }
+ });
+
+ it("should NOT register call_workflow tools when config.call_workflow is falsy", () => {
+ const tool = {
+ name: "generic_worker",
+ _call_workflow_name: "generic-worker",
+ description: "Call the 'generic-worker' workflow",
+ inputSchema: { type: "object", properties: {} },
+ };
+
+ // Config with falsy call_workflow value
+ const config = {
+ call_workflow: null,
+ missing_tool: {},
+ };
+
+ expect(shouldRegisterTool(tool, config)).toBe(false);
+ });
+
+ it("should register both call_workflow and dispatch_workflow tools when both configs exist", () => {
+ const callWorkflowTool = {
+ name: "generic_worker",
+ _call_workflow_name: "generic-worker",
+ description: "Call the 'generic-worker' workflow",
+ inputSchema: { type: "object", properties: {} },
+ };
+
+ const dispatchWorkflowTool = {
+ name: "release_workflow",
+ _workflow_name: "release-workflow",
+ description: "Dispatch the 'release-workflow' workflow",
+ inputSchema: { type: "object", properties: {} },
+ };
+
+ const config = {
+ call_workflow: { workflows: ["generic-worker"] },
+ dispatch_workflow: { workflows: ["release-workflow"] },
+ };
+
+ expect(shouldRegisterTool(callWorkflowTool, config)).toBe(true);
+ expect(shouldRegisterTool(dispatchWorkflowTool, config)).toBe(true);
+ });
+
+ it("should register call_workflow tool but NOT dispatch_workflow tool when only call_workflow config exists", () => {
+ const callWorkflowTool = {
+ name: "generic_worker",
+ _call_workflow_name: "generic-worker",
+ description: "Call the 'generic-worker' workflow",
+ inputSchema: { type: "object", properties: {} },
+ };
+
+ const dispatchWorkflowTool = {
+ name: "release_workflow",
+ _workflow_name: "release-workflow",
+ description: "Dispatch the 'release-workflow' workflow",
+ inputSchema: { type: "object", properties: {} },
+ };
+
+ const config = {
+ call_workflow: { workflows: ["generic-worker"] },
+ };
+
+ expect(shouldRegisterTool(callWorkflowTool, config)).toBe(true);
+ expect(shouldRegisterTool(dispatchWorkflowTool, config)).toBe(false);
+ });
+
+ it("should register dispatch_workflow tool but NOT call_workflow tool when only dispatch_workflow config exists", () => {
+ const callWorkflowTool = {
+ name: "generic_worker",
+ _call_workflow_name: "generic-worker",
+ description: "Call the 'generic-worker' workflow",
+ inputSchema: { type: "object", properties: {} },
+ };
+
+ const dispatchWorkflowTool = {
+ name: "release_workflow",
+ _workflow_name: "release-workflow",
+ description: "Dispatch the 'release-workflow' workflow",
+ inputSchema: { type: "object", properties: {} },
+ };
+
+ const config = {
+ dispatch_workflow: { workflows: ["release-workflow"] },
+ };
+
+ expect(shouldRegisterTool(callWorkflowTool, config)).toBe(false);
+ expect(shouldRegisterTool(dispatchWorkflowTool, config)).toBe(true);
+ });
+
+ it("should include call_workflow tools in tools/list when config.call_workflow is set", () => {
+ // Simulate the full tool list that would be registered on the server
+ const allTools = [
+ {
+ name: "missing_tool",
+ description: "Report a missing tool",
+ inputSchema: { type: "object", properties: {} },
+ },
+ {
+ name: "noop",
+ description: "No-op tool",
+ inputSchema: { type: "object", properties: {} },
+ },
+ {
+ name: "generic_worker",
+ _call_workflow_name: "generic-worker",
+ description: "Call the 'generic-worker' workflow",
+ inputSchema: { type: "object", properties: {} },
+ },
+ ];
+
+ const config = {
+ missing_tool: {},
+ noop: { max: 1 },
+ call_workflow: { workflows: ["generic-worker"] },
+ };
+
+ // Simulate registration by filtering tools using the registration logic
+ const registeredToolNames = allTools.filter(tool => shouldRegisterTool(tool, config)).map(tool => tool.name);
+
+ expect(registeredToolNames).toContain("missing_tool");
+ expect(registeredToolNames).toContain("noop");
+ expect(registeredToolNames).toContain("generic_worker");
+ expect(registeredToolNames).toHaveLength(3);
+ });
+
+ it("should exclude call_workflow tools from tools/list when config.call_workflow is absent", () => {
+ const allTools = [
+ {
+ name: "missing_tool",
+ description: "Report a missing tool",
+ inputSchema: { type: "object", properties: {} },
+ },
+ {
+ name: "generic_worker",
+ _call_workflow_name: "generic-worker",
+ description: "Call the 'generic-worker' workflow",
+ inputSchema: { type: "object", properties: {} },
+ },
+ ];
+
+ // Config without call_workflow
+ const config = {
+ missing_tool: {},
+ };
+
+ const registeredToolNames = allTools.filter(tool => shouldRegisterTool(tool, config)).map(tool => tool.name);
+
+ expect(registeredToolNames).toContain("missing_tool");
+ expect(registeredToolNames).not.toContain("generic_worker");
+ expect(registeredToolNames).toHaveLength(1);
+ });
+});
+
+/**
+ * Integration tests for call_workflow registration that exercise createMCPServer directly.
+ *
+ * These tests use real temp config/tools files (same pattern as safe_outputs_bootstrap.test.cjs)
+ * and verify the actual server.tools Map populated by createMCPServer.
+ */
+describe("safe_outputs_mcp_server_http createMCPServer call_workflow integration", () => {
+ let tempDir;
+
+ beforeEach(() => {
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "test-http-call-workflow-"));
+ });
+
+ afterEach(() => {
+ if (tempDir && fs.existsSync(tempDir)) {
+ fs.rmSync(tempDir, { recursive: true, force: true });
+ }
+ delete process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH;
+ delete process.env.GH_AW_SAFE_OUTPUTS_TOOLS_PATH;
+ delete process.env.GH_AW_SAFE_OUTPUTS;
+ });
+
+ it("should register a call_workflow tool when config.call_workflow is present", () => {
+ const configPath = path.join(tempDir, "config.json");
+ const toolsPath = path.join(tempDir, "tools.json");
+ const outputPath = path.join(tempDir, "output.jsonl");
+
+ fs.writeFileSync(
+ configPath,
+ JSON.stringify({
+ "call-workflow": { workflows: ["generic-worker"] },
+ })
+ );
+ fs.writeFileSync(
+ toolsPath,
+ JSON.stringify([
+ {
+ name: "generic_worker",
+ _call_workflow_name: "generic-worker",
+ description: "Call the 'generic-worker' workflow",
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
+ },
+ ])
+ );
+
+ process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH = configPath;
+ process.env.GH_AW_SAFE_OUTPUTS_TOOLS_PATH = toolsPath;
+ process.env.GH_AW_SAFE_OUTPUTS = outputPath;
+
+ const { server } = createMCPServer();
+
+ expect(server.tools.has("generic_worker")).toBe(true);
+ });
+
+ it("should NOT register a call_workflow tool when config.call_workflow is absent", () => {
+ const configPath = path.join(tempDir, "config.json");
+ const toolsPath = path.join(tempDir, "tools.json");
+ const outputPath = path.join(tempDir, "output.jsonl");
+
+ // Config without call-workflow key
+ fs.writeFileSync(configPath, JSON.stringify({ "missing-tool": {}, noop: { max: 1 } }));
+ fs.writeFileSync(
+ toolsPath,
+ JSON.stringify([
+ {
+ name: "generic_worker",
+ _call_workflow_name: "generic-worker",
+ description: "Call the 'generic-worker' workflow",
+ inputSchema: { type: "object", properties: {} },
+ },
+ ])
+ );
+
+ process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH = configPath;
+ process.env.GH_AW_SAFE_OUTPUTS_TOOLS_PATH = toolsPath;
+ process.env.GH_AW_SAFE_OUTPUTS = outputPath;
+
+ const { server } = createMCPServer();
+
+ expect(server.tools.has("generic_worker")).toBe(false);
+ });
+
+ it("should register all call_workflow tools for multiple configured workers", () => {
+ const configPath = path.join(tempDir, "config.json");
+ const toolsPath = path.join(tempDir, "tools.json");
+ const outputPath = path.join(tempDir, "output.jsonl");
+
+ fs.writeFileSync(
+ configPath,
+ JSON.stringify({
+ "call-workflow": { workflows: ["generic-worker", "specialist-worker"] },
+ })
+ );
+ fs.writeFileSync(
+ toolsPath,
+ JSON.stringify([
+ {
+ name: "generic_worker",
+ _call_workflow_name: "generic-worker",
+ description: "Call the 'generic-worker' workflow",
+ inputSchema: { type: "object", properties: {} },
+ },
+ {
+ name: "specialist_worker",
+ _call_workflow_name: "specialist-worker",
+ description: "Call the 'specialist-worker' workflow",
+ inputSchema: { type: "object", properties: {} },
+ },
+ ])
+ );
+
+ process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH = configPath;
+ process.env.GH_AW_SAFE_OUTPUTS_TOOLS_PATH = toolsPath;
+ process.env.GH_AW_SAFE_OUTPUTS = outputPath;
+
+ const { server } = createMCPServer();
+
+ expect(server.tools.has("generic_worker")).toBe(true);
+ expect(server.tools.has("specialist_worker")).toBe(true);
+ });
+
+ it("should register call_workflow and regular tools together from config", () => {
+ const configPath = path.join(tempDir, "config.json");
+ const toolsPath = path.join(tempDir, "tools.json");
+ const outputPath = path.join(tempDir, "output.jsonl");
+
+ fs.writeFileSync(
+ configPath,
+ JSON.stringify({
+ noop: { max: 1 },
+ "call-workflow": { workflows: ["generic-worker"] },
+ })
+ );
+ fs.writeFileSync(
+ toolsPath,
+ JSON.stringify([
+ {
+ name: "noop",
+ description: "No-op tool",
+ inputSchema: { type: "object", properties: {} },
+ },
+ {
+ name: "generic_worker",
+ _call_workflow_name: "generic-worker",
+ description: "Call the 'generic-worker' workflow",
+ inputSchema: { type: "object", properties: {} },
+ },
+ ])
+ );
+
+ process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH = configPath;
+ process.env.GH_AW_SAFE_OUTPUTS_TOOLS_PATH = toolsPath;
+ process.env.GH_AW_SAFE_OUTPUTS = outputPath;
+
+ const { server } = createMCPServer();
+
+ expect(server.tools.has("noop")).toBe(true);
+ expect(server.tools.has("generic_worker")).toBe(true);
+ });
+
+ it("should register call_workflow and dispatch_workflow tools independently", () => {
+ const configPath = path.join(tempDir, "config.json");
+ const toolsPath = path.join(tempDir, "tools.json");
+ const outputPath = path.join(tempDir, "output.jsonl");
+
+ fs.writeFileSync(
+ configPath,
+ JSON.stringify({
+ "call-workflow": { workflows: ["generic-worker"] },
+ dispatch_workflow: { workflows: ["release-workflow"] },
+ })
+ );
+ fs.writeFileSync(
+ toolsPath,
+ JSON.stringify([
+ {
+ name: "generic_worker",
+ _call_workflow_name: "generic-worker",
+ description: "Call the 'generic-worker' workflow",
+ inputSchema: { type: "object", properties: {} },
+ },
+ {
+ name: "release_workflow",
+ _workflow_name: "release-workflow",
+ description: "Dispatch the 'release-workflow' workflow",
+ inputSchema: { type: "object", properties: {} },
+ },
+ ])
+ );
+
+ process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH = configPath;
+ process.env.GH_AW_SAFE_OUTPUTS_TOOLS_PATH = toolsPath;
+ process.env.GH_AW_SAFE_OUTPUTS = outputPath;
+
+ const { server } = createMCPServer();
+
+ expect(server.tools.has("generic_worker")).toBe(true);
+ expect(server.tools.has("release_workflow")).toBe(true);
+ });
+
+ it("should return a call_workflow tool with a callable handler", async () => {
+ const configPath = path.join(tempDir, "config.json");
+ const toolsPath = path.join(tempDir, "tools.json");
+ const outputPath = path.join(tempDir, "output.jsonl");
+
+ fs.writeFileSync(
+ configPath,
+ JSON.stringify({
+ "call-workflow": { workflows: ["generic-worker"] },
+ })
+ );
+ fs.writeFileSync(
+ toolsPath,
+ JSON.stringify([
+ {
+ name: "generic_worker",
+ _call_workflow_name: "generic-worker",
+ description: "Call the 'generic-worker' workflow",
+ inputSchema: {
+ type: "object",
+ properties: { task: { type: "string", description: "Task to perform" } },
+ additionalProperties: false,
+ },
+ },
+ ])
+ );
+
+ process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH = configPath;
+ process.env.GH_AW_SAFE_OUTPUTS_TOOLS_PATH = toolsPath;
+ process.env.GH_AW_SAFE_OUTPUTS = outputPath;
+
+ const { server } = createMCPServer();
+ const tool = server.tools.get("generic_worker");
+
+ expect(tool).toBeDefined();
+ expect(typeof tool.handler).toBe("function");
+
+ // Calling the handler should produce a valid MCP result and write to the output file
+ const result = await tool.handler({ task: "do something" });
+
+ expect(result).toBeDefined();
+ expect(result.content).toBeDefined();
+ expect(result.isError).toBe(false);
+
+ // Output file should have been written with call_workflow type
+ const written = fs.readFileSync(outputPath, "utf8");
+ const entry = JSON.parse(written.trim());
+ expect(entry.type).toBe("call_workflow");
+ expect(entry.workflow_name).toBe("generic-worker");
+ });
+});
diff --git a/actions/setup/md/agentic_workflows_guide.md b/actions/setup/md/agentic_workflows_guide.md
new file mode 100644
index 00000000000..d573b55854e
--- /dev/null
+++ b/actions/setup/md/agentic_workflows_guide.md
@@ -0,0 +1,30 @@
+
+## Using the agentic-workflows MCP Server
+
+**⚠️ CRITICAL**: The `status`, `logs`, `audit`, and `compile` operations are MCP server tools,
+NOT shell commands. Do NOT run `gh aw` directly — it is not authenticated in this context.
+Do not attempt to download or build the `gh aw` extension. If the MCP server fails, give up.
+Call all operations as MCP tools with JSON parameters.
+
+- Run the `status` tool to verify configuration and list all workflows
+- Use the `logs` tool to download run logs (saves to `/tmp/gh-aw/aw-mcp/logs/`)
+- Use the `audit` tool with a run ID or URL to investigate specific runs
+
+### Tool Parameters
+
+#### `status` — Verify MCP server configuration and list workflows
+
+#### `logs` — Download workflow run logs
+- `workflow_name`: filter to a specific workflow (leave empty for all)
+- `count`: number of runs (default: 100)
+- `start_date`: filter runs after this date (YYYY-MM-DD or relative like `-1d`, `-7d`, `-30d`)
+- `end_date`: filter runs before this date
+- `engine`: filter by AI engine (`copilot`, `claude`, `codex`)
+- `branch`: filter by branch name
+- `firewall` / `no_firewall`: filter by firewall status
+- `after_run_id` / `before_run_id`: paginate by run database ID
+- Logs are saved to `/tmp/gh-aw/aw-mcp/logs/`
+
+#### `audit` — Inspect a specific run
+- `run_id_or_url`: numeric run ID, run URL, job URL, or job URL with step anchor
+
diff --git a/actions/setup/md/github_mcp_tools_prompt.md b/actions/setup/md/github_mcp_tools_prompt.md
new file mode 100644
index 00000000000..37878b225f5
--- /dev/null
+++ b/actions/setup/md/github_mcp_tools_prompt.md
@@ -0,0 +1,3 @@
+
+The GitHub MCP server is read-only. Use GitHub MCP tools for all GitHub reads: listing and searching issues, pull requests, discussions, labels, milestones; reading workflow runs, jobs, and artifacts; accessing repository contents, code, and metadata. Do not use shell `gh` commands for GitHub API reads — `gh` is not authenticated.
+
diff --git a/actions/setup/md/github_mcp_tools_with_safeoutputs_prompt.md b/actions/setup/md/github_mcp_tools_with_safeoutputs_prompt.md
new file mode 100644
index 00000000000..d9960b2e16c
--- /dev/null
+++ b/actions/setup/md/github_mcp_tools_with_safeoutputs_prompt.md
@@ -0,0 +1,3 @@
+
+The GitHub MCP server is read-only. Use GitHub MCP tools for all GitHub reads: listing and searching issues, pull requests, discussions, labels, milestones; reading workflow runs, jobs, and artifacts; accessing repository contents, code, and metadata. Do not use shell `gh` commands for GitHub API reads — `gh` is not authenticated. Use safeoutputs tools for GitHub writes and completion signaling.
+
diff --git a/actions/setup/md/safe_outputs_prompt.md b/actions/setup/md/safe_outputs_prompt.md
index 716b9227448..b7f6abee917 100644
--- a/actions/setup/md/safe_outputs_prompt.md
+++ b/actions/setup/md/safe_outputs_prompt.md
@@ -1,6 +1,6 @@
-gh CLI is NOT authenticated. Use safeoutputs MCP server tools for all GitHub operations — tool calls required.
+gh CLI is NOT authenticated. Use safeoutputs MCP server tools for GitHub writes and completion signaling — tool calls required.
**CRITICAL: You MUST call exactly one safe-output tool before finishing.** If no GitHub action was taken (no issues, comments, PRs, etc. were created or updated), you MUST call `noop` with a message explaining why no action was needed. Failing to call any safe-output tool is the #1 cause of workflow failures. Do NOT end your response without calling at least one safe-output tool.
diff --git a/cmd/gh-aw/main.go b/cmd/gh-aw/main.go
index 60220eb8b0c..80a3855aa08 100644
--- a/cmd/gh-aw/main.go
+++ b/cmd/gh-aw/main.go
@@ -161,7 +161,7 @@ Examples:
var removeCmd = &cobra.Command{
Use: "remove [pattern]",
- Short: "Remove agentic workflow files matching the given name prefix",
+ Short: "Remove agentic workflow files matching the given pattern",
Long: `Remove agentic workflow files matching the given workflow-id pattern.
The workflow-id is the basename of the Markdown file without the .md extension.
@@ -205,7 +205,7 @@ Examples:
var disableCmd = &cobra.Command{
Use: "disable [workflow]...",
- Short: "Disable agentic workflows and cancel any in-progress runs",
+ Short: "Disable agentic workflows",
Long: `Disable one or more workflows by ID, or all workflows if no IDs are provided.
Any in-progress runs will be cancelled before disabling.
` + cli.WorkflowIDExplanation + `
@@ -224,7 +224,7 @@ Examples:
var compileCmd = &cobra.Command{
Use: "compile [workflow]...",
- Short: "Compile agentic workflow files (.md) into GitHub Actions workflows (.lock.yml)",
+ Short: "Compile agentic workflow Markdown files into GitHub Actions YAML",
Long: `Compile one or more agentic workflows to YAML workflows.
If no workflows are specified, all Markdown files in .github/workflows will be compiled.
@@ -720,6 +720,7 @@ Use "` + string(constants.CLIExtensionPrefix) + ` help all" to show help for all
projectCmd := cli.NewProjectCommand()
checksCmd := cli.NewChecksCommand()
validateCmd := cli.NewValidateCommand(validateEngine)
+ domainsCmd := cli.NewDomainsCommand()
// Assign commands to groups
// Setup Commands
@@ -739,6 +740,7 @@ Use "` + string(constants.CLIExtensionPrefix) + ` help all" to show help for all
statusCmd.GroupID = "development"
listCmd.GroupID = "development"
fixCmd.GroupID = "development"
+ domainsCmd.GroupID = "development"
// Execution Commands
runCmd.GroupID = "execution"
@@ -790,6 +792,7 @@ Use "` + string(constants.CLIExtensionPrefix) + ` help all" to show help for all
rootCmd.AddCommand(completionCmd)
rootCmd.AddCommand(hashCmd)
rootCmd.AddCommand(projectCmd)
+ rootCmd.AddCommand(domainsCmd)
// Fix help flag descriptions for all subcommands to be consistent with the
// root command ("Show help for gh aw" vs the Cobra default "help for [cmd]").
@@ -839,7 +842,7 @@ func main() {
if isAlreadyFormatted {
fmt.Fprintln(os.Stderr, errMsg)
} else {
- fmt.Fprintln(os.Stderr, console.FormatErrorMessage(errMsg))
+ fmt.Fprintln(os.Stderr, console.FormatErrorChain(err))
}
os.Exit(1)
}
diff --git a/docs/package-lock.json b/docs/package-lock.json
index 48092658700..fd4654c77dc 100644
--- a/docs/package-lock.json
+++ b/docs/package-lock.json
@@ -8,24 +8,24 @@
"name": "docs",
"version": "0.0.1",
"dependencies": {
- "@astrojs/starlight": "^0.37.7",
+ "@astrojs/starlight": "^0.38.1",
"@primer/octicons": "^19.22.0",
- "astro": "^5.18.0",
+ "astro": "^6.0.5",
"astro-mermaid": "^1.3.1",
"mermaid": "^11.13.0",
"sharp": "^0.34.5",
- "starlight-blog": "^0.25.3",
- "starlight-changelogs": "^0.4.0",
- "starlight-links-validator": "^0.19.2",
- "starlight-llms-txt": "^0.7.0",
+ "starlight-blog": "^0.26.1",
+ "starlight-changelogs": "^0.5.0",
+ "starlight-links-validator": "^0.20.1",
+ "starlight-llms-txt": "^0.8.0",
"yaml": "^2.8.2"
},
"devDependencies": {
- "@marp-team/marp-cli": "^4.2.3",
+ "@marp-team/marp-cli": "^4.3.1",
"@playwright/test": "^1.58.2",
"@types/primer__octicons": "^19.21.0",
"http-server": "^14.1.1",
- "starlight-github-alerts": "^0.1.1"
+ "starlight-github-alerts": "^0.2.0"
}
},
"node_modules/@antfu/install-pkg": {
@@ -60,29 +60,31 @@
}
},
"node_modules/@astrojs/compiler": {
- "version": "2.13.0",
- "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.0.tgz",
- "integrity": "sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-3.0.1.tgz",
+ "integrity": "sha512-z97oYbdebO5aoWzuJ/8q5hLK232+17KcLZ7cJ8BCWk6+qNzVxn/gftC0KzMBUTD8WAaBkPpNSQK6PXLnNrZ0CA==",
"license": "MIT"
},
"node_modules/@astrojs/internal-helpers": {
- "version": "0.7.5",
- "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.5.tgz",
- "integrity": "sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA==",
- "license": "MIT"
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.8.0.tgz",
+ "integrity": "sha512-J56GrhEiV+4dmrGLPNOl2pZjpHXAndWVyiVDYGDuw6MWKpBSEMLdFxHzeM/6sqaknw9M+HFfHZAcvi3OfT3D/w==",
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^4.0.3"
+ }
},
"node_modules/@astrojs/markdown-remark": {
- "version": "6.3.10",
- "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.10.tgz",
- "integrity": "sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A==",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-7.0.0.tgz",
+ "integrity": "sha512-jTAXHPy45L7o1ljH4jYV+ShtOHtyQUa1mGp3a5fJp1soX8lInuTJQ6ihmldHzVM4Q7QptU4SzIDIcKbBJO7sXQ==",
"license": "MIT",
"dependencies": {
- "@astrojs/internal-helpers": "0.7.5",
- "@astrojs/prism": "3.3.0",
+ "@astrojs/internal-helpers": "0.8.0",
+ "@astrojs/prism": "4.0.0",
"github-slugger": "^2.0.0",
"hast-util-from-html": "^2.0.3",
"hast-util-to-text": "^4.0.2",
- "import-meta-resolve": "^4.2.0",
"js-yaml": "^4.1.1",
"mdast-util-definitions": "^6.0.0",
"rehype-raw": "^7.0.0",
@@ -91,25 +93,25 @@
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.2",
"remark-smartypants": "^3.0.2",
- "shiki": "^3.19.0",
- "smol-toml": "^1.5.2",
+ "shiki": "^4.0.0",
+ "smol-toml": "^1.6.0",
"unified": "^11.0.5",
"unist-util-remove-position": "^5.0.0",
- "unist-util-visit": "^5.0.0",
+ "unist-util-visit": "^5.1.0",
"unist-util-visit-parents": "^6.0.2",
"vfile": "^6.0.3"
}
},
"node_modules/@astrojs/mdx": {
- "version": "4.3.13",
- "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-4.3.13.tgz",
- "integrity": "sha512-IHDHVKz0JfKBy3//52JSiyWv089b7GVSChIXLrlUOoTLWowG3wr2/8hkaEgEyd/vysvNQvGk+QhysXpJW5ve6Q==",
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-5.0.1.tgz",
+ "integrity": "sha512-xfvc9MuV/5Kl6JaiYYEFi7ilbGYyaaOboH+gH8f2jHAZ2pmmHtnSrewS03vNeruBsa8rtS3X8NHJrqeZt+0wLg==",
"license": "MIT",
"dependencies": {
- "@astrojs/markdown-remark": "6.3.10",
+ "@astrojs/markdown-remark": "7.0.0",
"@mdx-js/mdx": "^3.1.1",
- "acorn": "^8.15.0",
- "es-module-lexer": "^1.7.0",
+ "acorn": "^8.16.0",
+ "es-module-lexer": "^2.0.0",
"estree-util-visit": "^2.0.0",
"hast-util-to-html": "^9.0.5",
"piccolore": "^0.1.3",
@@ -117,63 +119,64 @@
"remark-gfm": "^4.0.1",
"remark-smartypants": "^3.0.2",
"source-map": "^0.7.6",
- "unist-util-visit": "^5.0.0",
+ "unist-util-visit": "^5.1.0",
"vfile": "^6.0.3"
},
"engines": {
- "node": "18.20.8 || ^20.3.0 || >=22.0.0"
+ "node": "^20.19.1 || >=22.12.0"
},
"peerDependencies": {
- "astro": "^5.0.0"
+ "astro": "^6.0.0"
}
},
"node_modules/@astrojs/prism": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.3.0.tgz",
- "integrity": "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.0.tgz",
+ "integrity": "sha512-NndtNPpxaGinRpRytljGBvYHpTOwHycSZ/c+lQi5cHvkqqrHKWdkPEhImlODBNmbuB+vyQUNUDXyjzt66CihJg==",
"license": "MIT",
"dependencies": {
"prismjs": "^1.30.0"
},
"engines": {
- "node": "18.20.8 || ^20.3.0 || >=22.0.0"
+ "node": "^20.19.1 || >=22.12.0"
}
},
"node_modules/@astrojs/rss": {
- "version": "4.0.14",
- "resolved": "https://registry.npmjs.org/@astrojs/rss/-/rss-4.0.14.tgz",
- "integrity": "sha512-KCe1imDcADKOOuO/wtKOMDO/umsBD6DWF+94r5auna1jKl5fmlK9vzf+sjA3EyveXA/FoB3khtQ/u/tQgETmTw==",
+ "version": "4.0.17",
+ "resolved": "https://registry.npmjs.org/@astrojs/rss/-/rss-4.0.17.tgz",
+ "integrity": "sha512-eV+wdMbeVKC9+sPaV0LN8JL1LGo9YAh3GKl4Ou4nzMNLmXM/aswYpSGxVEAuHilgBZ6/++/Pv08ICmuOqX107w==",
"license": "MIT",
"dependencies": {
- "fast-xml-parser": "^5.3.0",
- "piccolore": "^0.1.3"
+ "fast-xml-parser": "5.4.1",
+ "piccolore": "^0.1.3",
+ "zod": "^4.3.6"
}
},
"node_modules/@astrojs/sitemap": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.5.1.tgz",
- "integrity": "sha512-uX5z52GLtQTgOe8r3jeGmFRYrFe52mdpLYJzqjvL1cdy5Kg3MLOZEvaZ/OCH0fSq0t7e50uJQ6oBMZG0ffszBg==",
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.1.tgz",
+ "integrity": "sha512-IzQqdTeskaMX+QDZCzMuJIp8A8C1vgzMBp/NmHNnadepHYNHcxQdGLQZYfkbd2EbRXUfOS+UDIKx8sKg0oWVdw==",
"license": "MIT",
"dependencies": {
- "sitemap": "^8.0.0",
+ "sitemap": "^9.0.0",
"stream-replace-string": "^2.0.0",
- "zod": "^3.24.4"
+ "zod": "^4.3.6"
}
},
"node_modules/@astrojs/starlight": {
- "version": "0.37.7",
- "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.37.7.tgz",
- "integrity": "sha512-KyBnou8aKIlPJUSNx6a1SN7XyH22oj/VAvTGC+Edld4Bnei1A//pmCRTBvSrSeoGrdUjK0ErFUfaEhhO1bPfDg==",
+ "version": "0.38.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.38.1.tgz",
+ "integrity": "sha512-CATPH4Dy44OYAJhoyUHh6NqpColWEVufanGVwnM0l/bcaNMo5V/rypwL0Vu0Edp+ZIXE7/1DA9CrNj5jmCVSLQ==",
"license": "MIT",
"dependencies": {
- "@astrojs/markdown-remark": "^6.3.1",
- "@astrojs/mdx": "^4.2.3",
- "@astrojs/sitemap": "^3.3.0",
+ "@astrojs/markdown-remark": "^7.0.0",
+ "@astrojs/mdx": "^5.0.0",
+ "@astrojs/sitemap": "^3.7.1",
"@pagefind/default-ui": "^1.3.0",
"@types/hast": "^3.0.4",
"@types/js-yaml": "^4.0.9",
"@types/mdast": "^4.0.4",
- "astro-expressive-code": "^0.41.1",
+ "astro-expressive-code": "^0.41.6",
"bcp-47": "^2.1.0",
"hast-util-from-html": "^2.0.1",
"hast-util-select": "^6.0.2",
@@ -196,7 +199,7 @@
"vfile": "^6.0.2"
},
"peerDependencies": {
- "astro": "^5.5.0"
+ "astro": "^6.0.0"
}
},
"node_modules/@astrojs/telemetry": {
@@ -218,13 +221,13 @@
}
},
"node_modules/@babel/code-frame": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
- "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
+ "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
@@ -251,12 +254,12 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
- "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
+ "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.28.5"
+ "@babel/types": "^7.29.0"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -275,9 +278,9 @@
}
},
"node_modules/@babel/types": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
- "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
+ "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
@@ -344,6 +347,25 @@
"integrity": "sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA==",
"license": "Apache-2.0"
},
+ "node_modules/@clack/core": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.1.0.tgz",
+ "integrity": "sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA==",
+ "license": "MIT",
+ "dependencies": {
+ "sisteransi": "^1.0.5"
+ }
+ },
+ "node_modules/@clack/prompts": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.1.0.tgz",
+ "integrity": "sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g==",
+ "license": "MIT",
+ "dependencies": {
+ "@clack/core": "1.1.0",
+ "sisteransi": "^1.0.5"
+ }
+ },
"node_modules/@csstools/postcss-is-pseudo-class": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.3.tgz",
@@ -371,10 +393,10 @@
"postcss": "^8.4"
}
},
- "node_modules/@csstools/postcss-is-pseudo-class/node_modules/@csstools/selector-specificity": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz",
- "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==",
+ "node_modules/@csstools/selector-resolve-nested": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz",
+ "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==",
"dev": true,
"funding": [
{
@@ -394,24 +416,33 @@
"postcss-selector-parser": "^7.0.0"
}
},
- "node_modules/@csstools/postcss-is-pseudo-class/node_modules/postcss-selector-parser": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
- "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
+ "node_modules/@csstools/selector-specificity": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz",
+ "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "cssesc": "^3.0.0",
- "util-deprecate": "^1.0.2"
- },
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
"engines": {
- "node": ">=4"
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss-selector-parser": "^7.0.0"
}
},
"node_modules/@ctrl/tinycolor": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.1.0.tgz",
- "integrity": "sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz",
+ "integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==",
"license": "MIT",
"engines": {
"node": ">=14"
@@ -428,9 +459,9 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
- "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz",
+ "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==",
"cpu": [
"ppc64"
],
@@ -444,9 +475,9 @@
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
- "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz",
+ "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==",
"cpu": [
"arm"
],
@@ -460,9 +491,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
- "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz",
+ "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==",
"cpu": [
"arm64"
],
@@ -476,9 +507,9 @@
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
- "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz",
+ "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==",
"cpu": [
"x64"
],
@@ -492,9 +523,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
- "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz",
+ "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==",
"cpu": [
"arm64"
],
@@ -508,9 +539,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
- "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz",
+ "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==",
"cpu": [
"x64"
],
@@ -524,9 +555,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
- "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz",
+ "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==",
"cpu": [
"arm64"
],
@@ -540,9 +571,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
- "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz",
+ "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==",
"cpu": [
"x64"
],
@@ -556,9 +587,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
- "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz",
+ "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==",
"cpu": [
"arm"
],
@@ -572,9 +603,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
- "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz",
+ "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==",
"cpu": [
"arm64"
],
@@ -588,9 +619,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
- "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz",
+ "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==",
"cpu": [
"ia32"
],
@@ -604,9 +635,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
- "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz",
+ "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==",
"cpu": [
"loong64"
],
@@ -620,9 +651,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
- "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz",
+ "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==",
"cpu": [
"mips64el"
],
@@ -636,9 +667,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
- "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz",
+ "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==",
"cpu": [
"ppc64"
],
@@ -652,9 +683,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
- "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz",
+ "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==",
"cpu": [
"riscv64"
],
@@ -668,9 +699,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
- "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz",
+ "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==",
"cpu": [
"s390x"
],
@@ -684,9 +715,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
- "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz",
+ "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==",
"cpu": [
"x64"
],
@@ -700,9 +731,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
- "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz",
+ "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==",
"cpu": [
"arm64"
],
@@ -716,9 +747,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
- "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz",
+ "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==",
"cpu": [
"x64"
],
@@ -732,9 +763,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
- "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz",
+ "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==",
"cpu": [
"arm64"
],
@@ -748,9 +779,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
- "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz",
+ "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==",
"cpu": [
"x64"
],
@@ -764,9 +795,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
- "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz",
+ "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==",
"cpu": [
"arm64"
],
@@ -780,9 +811,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
- "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz",
+ "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==",
"cpu": [
"x64"
],
@@ -796,9 +827,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
- "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz",
+ "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==",
"cpu": [
"arm64"
],
@@ -812,9 +843,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
- "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz",
+ "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==",
"cpu": [
"ia32"
],
@@ -828,9 +859,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
- "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz",
+ "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==",
"cpu": [
"x64"
],
@@ -844,9 +875,9 @@
}
},
"node_modules/@expressive-code/core": {
- "version": "0.41.3",
- "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.41.3.tgz",
- "integrity": "sha512-9qzohqU7O0+JwMEEgQhnBPOw5DtsQRBXhW++5fvEywsuX44vCGGof1SL5OvPElvNgaWZ4pFZAFSlkNOkGyLwSQ==",
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.41.7.tgz",
+ "integrity": "sha512-ck92uZYZ9Wba2zxkiZLsZGi9N54pMSAVdrI9uW3Oo9AtLglD5RmrdTwbYPCT2S/jC36JGB2i+pnQtBm/Ib2+dg==",
"license": "MIT",
"dependencies": {
"@ctrl/tinycolor": "^4.0.4",
@@ -861,31 +892,108 @@
}
},
"node_modules/@expressive-code/plugin-frames": {
- "version": "0.41.3",
- "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.41.3.tgz",
- "integrity": "sha512-rFQtmf/3N2CK3Cq/uERweMTYZnBu+CwxBdHuOftEmfA9iBE7gTVvwpbh82P9ZxkPLvc40UMhYt7uNuAZexycRQ==",
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.41.7.tgz",
+ "integrity": "sha512-diKtxjQw/979cTglRFaMCY/sR6hWF0kSMg8jsKLXaZBSfGS0I/Hoe7Qds3vVEgeoW+GHHQzMcwvgx/MOIXhrTA==",
"license": "MIT",
"dependencies": {
- "@expressive-code/core": "^0.41.3"
+ "@expressive-code/core": "^0.41.7"
}
},
"node_modules/@expressive-code/plugin-shiki": {
- "version": "0.41.3",
- "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.41.3.tgz",
- "integrity": "sha512-RlTARoopzhFJIOVHLGvuXJ8DCEme/hjV+ZnRJBIxzxsKVpGPW4Oshqg9xGhWTYdHstTsxO663s0cdBLzZj9TQA==",
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.41.7.tgz",
+ "integrity": "sha512-DL605bLrUOgqTdZ0Ot5MlTaWzppRkzzqzeGEu7ODnHF39IkEBbFdsC7pbl3LbUQ1DFtnfx6rD54k/cdofbW6KQ==",
"license": "MIT",
"dependencies": {
- "@expressive-code/core": "^0.41.3",
+ "@expressive-code/core": "^0.41.7",
"shiki": "^3.2.2"
}
},
+ "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/core": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.23.0.tgz",
+ "integrity": "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4",
+ "hast-util-to-html": "^9.0.5"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/engine-javascript": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz",
+ "integrity": "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "oniguruma-to-es": "^4.3.4"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/engine-oniguruma": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz",
+ "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/langs": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz",
+ "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.23.0"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/themes": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz",
+ "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.23.0"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/types": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz",
+ "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/shiki": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.23.0.tgz",
+ "integrity": "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/core": "3.23.0",
+ "@shikijs/engine-javascript": "3.23.0",
+ "@shikijs/engine-oniguruma": "3.23.0",
+ "@shikijs/langs": "3.23.0",
+ "@shikijs/themes": "3.23.0",
+ "@shikijs/types": "3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
"node_modules/@expressive-code/plugin-text-markers": {
- "version": "0.41.3",
- "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.41.3.tgz",
- "integrity": "sha512-SN8tkIzDpA0HLAscEYD2IVrfLiid6qEdE9QLlGVSxO1KEw7qYvjpbNBQjUjMr5/jvTJ7ys6zysU2vLPHE0sb2g==",
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.41.7.tgz",
+ "integrity": "sha512-Ewpwuc5t6eFdZmWlFyeuy3e1PTQC0jFvw2Q+2bpcWXbOZhPLsT7+h8lsSIJxb5mS7wZko7cKyQ2RLYDyK6Fpmw==",
"license": "MIT",
"dependencies": {
- "@expressive-code/core": "^0.41.3"
+ "@expressive-code/core": "^0.41.7"
}
},
"node_modules/@iconify/types": {
@@ -1173,20 +1281,20 @@
"license": "MIT"
},
"node_modules/@marp-team/marp-cli": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/@marp-team/marp-cli/-/marp-cli-4.2.3.tgz",
- "integrity": "sha512-yfEIkF7mlumg8CVV5m/UkLEDkW/ayM/SD6Bo8fbAvdscBQ/l9D44/aNFJsiqgNtjfktzpvRqjcBXNhWD0YTq5Q==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@marp-team/marp-cli/-/marp-cli-4.3.1.tgz",
+ "integrity": "sha512-REj2PUTDhHiFsKxzh8AF90YrApEbNcOU7eZ8TKF3sF0b5O91lyaZMH2E5y7o/bfdFP70N8l7vyqXA4pdVQ+EIA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@marp-team/marp-core": "^4.1.0",
- "@marp-team/marpit": "^3.1.3",
+ "@marp-team/marp-core": "^4.3.0",
+ "@marp-team/marpit": "^3.2.1",
"chokidar": "^4.0.3",
- "cosmiconfig": "^9.0.0",
- "puppeteer-core": "^24.16.0",
- "serve-index": "^1.9.1",
+ "cosmiconfig": "^9.0.1",
+ "puppeteer-core": "^24.39.1",
+ "serve-index": "^1.9.2",
"tmp": "^0.2.5",
- "ws": "^8.18.3",
+ "ws": "^8.19.0",
"yargs": "^17.7.2"
},
"bin": {
@@ -1197,50 +1305,36 @@
}
},
"node_modules/@marp-team/marp-core": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/@marp-team/marp-core/-/marp-core-4.2.0.tgz",
- "integrity": "sha512-AoqRk9g6kF44OhdgV2eUsc0ciyxkI3IM/S4ntV+aHy59NSTcwPEHrbtAzqe+q2PK/trmQX233/0plusNZPoF+Q==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@marp-team/marp-core/-/marp-core-4.3.0.tgz",
+ "integrity": "sha512-Qwd0wsHS+7bdCBmmnxCU2OKclCSdsVu4U474zSs44Y2SMXr6d9wBYAMD5IXI4dbmaB57Ez5fdPUxPZZsFo8Tmw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@marp-team/marpit": "^3.2.0",
+ "@marp-team/marpit": "^3.2.1",
"@marp-team/marpit-svg-polyfill": "^2.1.0",
"highlight.js": "^11.11.1",
- "katex": "^0.16.25",
+ "katex": "^0.16.33",
"mathjax-full": "^3.2.2",
- "postcss-selector-parser": "^7.1.0",
+ "postcss-selector-parser": "^7.1.1",
"xss": "^1.0.15"
},
"engines": {
"node": ">=18"
}
},
- "node_modules/@marp-team/marp-core/node_modules/postcss-selector-parser": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
- "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cssesc": "^3.0.0",
- "util-deprecate": "^1.0.2"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/@marp-team/marpit": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/@marp-team/marpit/-/marpit-3.2.0.tgz",
- "integrity": "sha512-DNCbwkAKugzCtiHJg/7DciIRwnKwAI2QH3VWWC1cVxoBBQTPnH5D9HcWqpDdduUqnCuW2PY78afVo+QlaInDdQ==",
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/@marp-team/marpit/-/marpit-3.2.1.tgz",
+ "integrity": "sha512-MflN1movsh8PXJbM9otjJOtlcB0vSrvw5PgXs2PDguVYVzKQmGpiy9QHHKq0rryrgaSp376TdJWBP7mXHHAG8w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@csstools/postcss-is-pseudo-class": "^5.0.3",
"cssesc": "^3.0.0",
- "js-yaml": "^4.1.0",
+ "js-yaml": "^4.1.1",
"lodash.kebabcase": "^4.1.1",
- "markdown-it": "^14.1.0",
+ "markdown-it": "^14.1.1",
"markdown-it-front-matter": "^0.2.4",
"postcss": "^8.5.6",
"postcss-nesting": "^13.0.2"
@@ -1429,9 +1523,9 @@
}
},
"node_modules/@puppeteer/browsers": {
- "version": "2.11.0",
- "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.11.0.tgz",
- "integrity": "sha512-n6oQX6mYkG8TRPuPXmbPidkUbsSRalhmaaVAQxvH1IkQy63cwsH+kOjB3e4cpCDHg0aSvsiX9bQ4s2VB6mGWUQ==",
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.0.tgz",
+ "integrity": "sha512-46BZJYJjc/WwmKjsvDFykHtXrtomsCIrwYQPOP7VfMJoZY2bsDF9oROBABR3paDjDcmkUye1Pb1BqdcdiipaWA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -1439,7 +1533,7 @@
"extract-zip": "^2.0.1",
"progress": "^2.0.3",
"proxy-agent": "^6.5.0",
- "semver": "^7.7.3",
+ "semver": "^7.7.4",
"tar-fs": "^3.1.1",
"yargs": "^17.7.2"
},
@@ -1804,64 +1898,97 @@
]
},
"node_modules/@shikijs/core": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.21.0.tgz",
- "integrity": "sha512-AXSQu/2n1UIQekY8euBJlvFYZIw0PHY63jUzGbrOma4wPxzznJXTXkri+QcHeBNaFxiiOljKxxJkVSoB3PjbyA==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.0.2.tgz",
+ "integrity": "sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==",
"license": "MIT",
"dependencies": {
- "@shikijs/types": "3.21.0",
+ "@shikijs/primitive": "4.0.2",
+ "@shikijs/types": "4.0.2",
"@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4",
"hast-util-to-html": "^9.0.5"
+ },
+ "engines": {
+ "node": ">=20"
}
},
"node_modules/@shikijs/engine-javascript": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.21.0.tgz",
- "integrity": "sha512-ATwv86xlbmfD9n9gKRiwuPpWgPENAWCLwYCGz9ugTJlsO2kOzhOkvoyV/UD+tJ0uT7YRyD530x6ugNSffmvIiQ==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.0.2.tgz",
+ "integrity": "sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag==",
"license": "MIT",
"dependencies": {
- "@shikijs/types": "3.21.0",
+ "@shikijs/types": "4.0.2",
"@shikijs/vscode-textmate": "^10.0.2",
"oniguruma-to-es": "^4.3.4"
+ },
+ "engines": {
+ "node": ">=20"
}
},
"node_modules/@shikijs/engine-oniguruma": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.21.0.tgz",
- "integrity": "sha512-OYknTCct6qiwpQDqDdf3iedRdzj6hFlOPv5hMvI+hkWfCKs5mlJ4TXziBG9nyabLwGulrUjHiCq3xCspSzErYQ==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.0.2.tgz",
+ "integrity": "sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg==",
"license": "MIT",
"dependencies": {
- "@shikijs/types": "3.21.0",
+ "@shikijs/types": "4.0.2",
"@shikijs/vscode-textmate": "^10.0.2"
+ },
+ "engines": {
+ "node": ">=20"
}
},
"node_modules/@shikijs/langs": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.21.0.tgz",
- "integrity": "sha512-g6mn5m+Y6GBJ4wxmBYqalK9Sp0CFkUqfNzUy2pJglUginz6ZpWbaWjDB4fbQ/8SHzFjYbtU6Ddlp1pc+PPNDVA==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.0.2.tgz",
+ "integrity": "sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "4.0.2"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@shikijs/primitive": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.0.2.tgz",
+ "integrity": "sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw==",
"license": "MIT",
"dependencies": {
- "@shikijs/types": "3.21.0"
+ "@shikijs/types": "4.0.2",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=20"
}
},
"node_modules/@shikijs/themes": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.21.0.tgz",
- "integrity": "sha512-BAE4cr9EDiZyYzwIHEk7JTBJ9CzlPuM4PchfcA5ao1dWXb25nv6hYsoDiBq2aZK9E3dlt3WB78uI96UESD+8Mw==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.0.2.tgz",
+ "integrity": "sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA==",
"license": "MIT",
"dependencies": {
- "@shikijs/types": "3.21.0"
+ "@shikijs/types": "4.0.2"
+ },
+ "engines": {
+ "node": ">=20"
}
},
"node_modules/@shikijs/types": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.21.0.tgz",
- "integrity": "sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.0.2.tgz",
+ "integrity": "sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg==",
"license": "MIT",
"dependencies": {
"@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=20"
}
},
"node_modules/@shikijs/vscode-textmate": {
@@ -2221,18 +2348,18 @@
}
},
"node_modules/@types/node": {
- "version": "24.3.1",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz",
- "integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==",
+ "version": "25.5.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
+ "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
"license": "MIT",
"dependencies": {
- "undici-types": "~7.10.0"
+ "undici-types": "~7.18.0"
}
},
"node_modules/@types/picomatch": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-3.0.2.tgz",
- "integrity": "sha512-n0i8TD3UDB7paoMMxA3Y65vUncFJXjcUf7lQY7YyKGl6031FNjfsLs6pdLFCy2GNFxItPJG8GvvpbZc2skH7WA==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==",
"license": "MIT"
},
"node_modules/@types/primer__octicons": {
@@ -2316,9 +2443,9 @@
}
},
"node_modules/acorn": {
- "version": "8.15.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
- "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -2346,56 +2473,6 @@
"node": ">= 14"
}
},
- "node_modules/ansi-align": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
- "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
- "license": "ISC",
- "dependencies": {
- "string-width": "^4.1.0"
- }
- },
- "node_modules/ansi-align/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ansi-align/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT"
- },
- "node_modules/ansi-align/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ansi-align/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/ansi-escapes": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz",
@@ -2412,24 +2489,26 @@
}
},
"node_modules/ansi-regex": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
- "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
"license": "MIT",
"engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ "node": ">=8"
}
},
"node_modules/ansi-styles": {
- "version": "6.2.3",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
- "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
"license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
"engines": {
- "node": ">=12"
+ "node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
@@ -2514,80 +2593,72 @@
}
},
"node_modules/astro": {
- "version": "5.18.0",
- "resolved": "https://registry.npmjs.org/astro/-/astro-5.18.0.tgz",
- "integrity": "sha512-CHiohwJIS4L0G6/IzE1Fx3dgWqXBCXus/od0eGUfxrZJD2um2pE7ehclMmgL/fXqbU7NfE1Ze2pq34h2QaA6iQ==",
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/astro/-/astro-6.0.5.tgz",
+ "integrity": "sha512-JnLCwaoCaRXIHuIB8yNztJrd7M3hXrHUMAoQmeXtEBKxRu/738REhaCZ1lapjrS9HlpHsWTu3JUXTERB/0PA7g==",
"license": "MIT",
"dependencies": {
- "@astrojs/compiler": "^2.13.0",
- "@astrojs/internal-helpers": "0.7.5",
- "@astrojs/markdown-remark": "6.3.10",
+ "@astrojs/compiler": "^3.0.0",
+ "@astrojs/internal-helpers": "0.8.0",
+ "@astrojs/markdown-remark": "7.0.0",
"@astrojs/telemetry": "3.3.0",
"@capsizecss/unpack": "^4.0.0",
+ "@clack/prompts": "^1.0.1",
"@oslojs/encoding": "^1.1.0",
"@rollup/pluginutils": "^5.3.0",
- "acorn": "^8.15.0",
"aria-query": "^5.3.2",
"axobject-query": "^4.1.0",
- "boxen": "8.0.1",
- "ci-info": "^4.3.1",
+ "ci-info": "^4.4.0",
"clsx": "^2.1.1",
- "common-ancestor-path": "^1.0.1",
+ "common-ancestor-path": "^2.0.0",
"cookie": "^1.1.1",
- "cssesc": "^3.0.0",
- "debug": "^4.4.3",
- "deterministic-object-hash": "^2.0.2",
- "devalue": "^5.6.2",
+ "devalue": "^5.6.3",
"diff": "^8.0.3",
"dlv": "^1.1.3",
"dset": "^3.1.4",
- "es-module-lexer": "^1.7.0",
+ "es-module-lexer": "^2.0.0",
"esbuild": "^0.27.3",
- "estree-walker": "^3.0.3",
"flattie": "^1.1.1",
- "fontace": "~0.4.0",
+ "fontace": "~0.4.1",
"github-slugger": "^2.0.0",
"html-escaper": "3.0.3",
"http-cache-semantics": "^4.2.0",
- "import-meta-resolve": "^4.2.0",
"js-yaml": "^4.1.1",
"magic-string": "^0.30.21",
- "magicast": "^0.5.1",
+ "magicast": "^0.5.2",
"mrmime": "^2.0.1",
"neotraverse": "^0.6.18",
- "p-limit": "^6.2.0",
- "p-queue": "^8.1.1",
+ "obug": "^2.1.1",
+ "p-limit": "^7.3.0",
+ "p-queue": "^9.1.0",
"package-manager-detector": "^1.6.0",
"piccolore": "^0.1.3",
"picomatch": "^4.0.3",
- "prompts": "^2.4.2",
"rehype": "^13.0.2",
- "semver": "^7.7.3",
- "shiki": "^3.21.0",
+ "semver": "^7.7.4",
+ "shiki": "^4.0.0",
"smol-toml": "^1.6.0",
"svgo": "^4.0.0",
+ "tinyclip": "^0.1.6",
"tinyexec": "^1.0.2",
"tinyglobby": "^0.2.15",
"tsconfck": "^3.1.6",
"ultrahtml": "^1.6.0",
- "unifont": "~0.7.3",
- "unist-util-visit": "^5.0.0",
+ "unifont": "~0.7.4",
+ "unist-util-visit": "^5.1.0",
"unstorage": "^1.17.4",
"vfile": "^6.0.3",
- "vite": "^6.4.1",
- "vitefu": "^1.1.1",
+ "vite": "^7.3.1",
+ "vitefu": "^1.1.2",
"xxhash-wasm": "^1.1.0",
- "yargs-parser": "^21.1.1",
- "yocto-spinner": "^0.2.3",
- "zod": "^3.25.76",
- "zod-to-json-schema": "^3.25.1",
- "zod-to-ts": "^1.2.0"
+ "yargs-parser": "^22.0.0",
+ "zod": "^4.3.6"
},
"bin": {
- "astro": "astro.js"
+ "astro": "bin/astro.mjs"
},
"engines": {
- "node": "18.20.8 || ^20.3.0 || >=22.0.0",
+ "node": "^20.19.1 || >=22.12.0",
"npm": ">=9.6.5",
"pnpm": ">=7.1.0"
},
@@ -2600,15 +2671,15 @@
}
},
"node_modules/astro-expressive-code": {
- "version": "0.41.3",
- "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.41.3.tgz",
- "integrity": "sha512-u+zHMqo/QNLE2eqYRCrK3+XMlKakv33Bzuz+56V1gs8H0y6TZ0hIi3VNbIxeTn51NLn+mJfUV/A0kMNfE4rANw==",
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.41.7.tgz",
+ "integrity": "sha512-hUpogGc6DdAd+I7pPXsctyYPRBJDK7Q7d06s4cyP0Vz3OcbziP3FNzN0jZci1BpCvLn9675DvS7B9ctKKX64JQ==",
"license": "MIT",
"dependencies": {
- "rehype-expressive-code": "^0.41.3"
+ "rehype-expressive-code": "^0.41.7"
},
"peerDependencies": {
- "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0"
+ "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta"
}
},
"node_modules/astro-mermaid": {
@@ -2672,483 +2743,26 @@
"node": ">= 18"
}
},
- "node_modules/astro/node_modules/@esbuild/aix-ppc64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
- "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
- "cpu": [
- "ppc64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "aix"
- ],
+ "node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/axobject-query": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+ "license": "Apache-2.0",
"engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/android-arm": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
- "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
- "cpu": [
- "arm"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/android-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
- "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/android-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
- "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/darwin-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
- "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/darwin-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
- "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/freebsd-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
- "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/freebsd-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
- "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/linux-arm": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
- "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
- "cpu": [
- "arm"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/linux-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
- "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/linux-ia32": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
- "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
- "cpu": [
- "ia32"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/linux-loong64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
- "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
- "cpu": [
- "loong64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/linux-mips64el": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
- "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
- "cpu": [
- "mips64el"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/linux-ppc64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
- "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
- "cpu": [
- "ppc64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/linux-riscv64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
- "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
- "cpu": [
- "riscv64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/linux-s390x": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
- "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
- "cpu": [
- "s390x"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/linux-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
- "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/netbsd-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
- "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/netbsd-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
- "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/openbsd-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
- "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/openbsd-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
- "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/openharmony-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
- "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/sunos-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
- "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/win32-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
- "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/win32-ia32": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
- "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
- "cpu": [
- "ia32"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/@esbuild/win32-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
- "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/astro/node_modules/esbuild": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
- "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
- "hasInstallScript": true,
- "license": "MIT",
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.27.3",
- "@esbuild/android-arm": "0.27.3",
- "@esbuild/android-arm64": "0.27.3",
- "@esbuild/android-x64": "0.27.3",
- "@esbuild/darwin-arm64": "0.27.3",
- "@esbuild/darwin-x64": "0.27.3",
- "@esbuild/freebsd-arm64": "0.27.3",
- "@esbuild/freebsd-x64": "0.27.3",
- "@esbuild/linux-arm": "0.27.3",
- "@esbuild/linux-arm64": "0.27.3",
- "@esbuild/linux-ia32": "0.27.3",
- "@esbuild/linux-loong64": "0.27.3",
- "@esbuild/linux-mips64el": "0.27.3",
- "@esbuild/linux-ppc64": "0.27.3",
- "@esbuild/linux-riscv64": "0.27.3",
- "@esbuild/linux-s390x": "0.27.3",
- "@esbuild/linux-x64": "0.27.3",
- "@esbuild/netbsd-arm64": "0.27.3",
- "@esbuild/netbsd-x64": "0.27.3",
- "@esbuild/openbsd-arm64": "0.27.3",
- "@esbuild/openbsd-x64": "0.27.3",
- "@esbuild/openharmony-arm64": "0.27.3",
- "@esbuild/sunos-x64": "0.27.3",
- "@esbuild/win32-arm64": "0.27.3",
- "@esbuild/win32-ia32": "0.27.3",
- "@esbuild/win32-x64": "0.27.3"
- }
- },
- "node_modules/async": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
- "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/axobject-query": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
- "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">= 0.4"
+ "node": ">= 0.4"
}
},
"node_modules/b4a": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz",
- "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==",
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz",
+ "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==",
"dev": true,
"license": "Apache-2.0",
"peerDependencies": {
@@ -3186,12 +2800,11 @@
}
},
"node_modules/bare-fs": {
- "version": "4.5.2",
- "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.2.tgz",
- "integrity": "sha512-veTnRzkb6aPHOvSKIOy60KzURfBdUflr5VReI+NSaPL6xf+XLdONQgZgpYvUuZLVQ8dCqxpBAudaOM1+KpAUxw==",
+ "version": "4.5.5",
+ "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.5.tgz",
+ "integrity": "sha512-XvwYM6VZqKoqDll8BmSww5luA5eflDzY0uEFfBJtFKe4PAAtxBjU3YIxzIBzhyaEQBy1VXEQBto4cpN5RZJw+w==",
"dev": true,
"license": "Apache-2.0",
- "optional": true,
"dependencies": {
"bare-events": "^2.5.4",
"bare-path": "^3.0.0",
@@ -3212,12 +2825,11 @@
}
},
"node_modules/bare-os": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz",
- "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==",
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.8.0.tgz",
+ "integrity": "sha512-Dc9/SlwfxkXIGYhvMQNUtKaXCaGkZYGcd1vuNUUADVqzu4/vQfvnMkYYOUnt2VwQ2AqKr/8qAVFRtwETljgeFg==",
"dev": true,
"license": "Apache-2.0",
- "optional": true,
"engines": {
"bare": ">=1.14.0"
}
@@ -3228,20 +2840,19 @@
"integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==",
"dev": true,
"license": "Apache-2.0",
- "optional": true,
"dependencies": {
"bare-os": "^3.0.1"
}
},
"node_modules/bare-stream": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz",
- "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==",
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.8.1.tgz",
+ "integrity": "sha512-bSeR8RfvbRwDpD7HWZvn8M3uYNDrk7m9DQjYOFkENZlXW8Ju/MPaqUPQq5LqJ3kyjEm07siTaAQ7wBKCU59oHg==",
"dev": true,
"license": "Apache-2.0",
- "optional": true,
"dependencies": {
- "streamx": "^2.21.0"
+ "streamx": "^2.21.0",
+ "teex": "^1.0.1"
},
"peerDependencies": {
"bare-buffer": "*",
@@ -3262,17 +2873,10 @@
"integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==",
"dev": true,
"license": "Apache-2.0",
- "optional": true,
"dependencies": {
"bare-path": "^3.0.0"
}
},
- "node_modules/base-64": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz",
- "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==",
- "license": "MIT"
- },
"node_modules/basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
@@ -3287,9 +2891,9 @@
}
},
"node_modules/basic-ftp": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
- "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==",
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz",
+ "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3334,28 +2938,6 @@
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
"license": "ISC"
},
- "node_modules/boxen": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz",
- "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==",
- "license": "MIT",
- "dependencies": {
- "ansi-align": "^3.0.1",
- "camelcase": "^8.0.0",
- "chalk": "^5.3.0",
- "cli-boxes": "^3.0.0",
- "string-width": "^7.2.0",
- "type-fest": "^4.21.0",
- "widest-line": "^5.0.0",
- "wrap-ansi": "^9.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
@@ -3419,18 +3001,6 @@
"node": ">=6"
}
},
- "node_modules/camelcase": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
- "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==",
- "license": "MIT",
- "engines": {
- "node": ">=16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/ccount": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
@@ -3441,18 +3011,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/chalk": {
- "version": "5.6.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
- "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
- "license": "MIT",
- "engines": {
- "node": "^12.17.0 || ^14.13 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
"node_modules/character-entities": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
@@ -3510,164 +3068,83 @@
"node_modules/chevrotain-allstar": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz",
- "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==",
- "license": "MIT",
- "dependencies": {
- "lodash-es": "^4.17.21"
- },
- "peerDependencies": {
- "chevrotain": "^11.0.0"
- }
- },
- "node_modules/chokidar": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
- "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "readdirp": "^4.0.1"
- },
- "engines": {
- "node": ">= 14.16.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- }
- },
- "node_modules/chromium-bidi": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-11.0.0.tgz",
- "integrity": "sha512-cM3DI+OOb89T3wO8cpPSro80Q9eKYJ7hGVXoGS3GkDPxnYSqiv+6xwpIf6XERyJ9Tdsl09hmNmY94BkgZdVekw==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "mitt": "^3.0.1",
- "zod": "^3.24.1"
- },
- "peerDependencies": {
- "devtools-protocol": "*"
- }
- },
- "node_modules/ci-info": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz",
- "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/sibiraj-s"
- }
- ],
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/cli-boxes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
- "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/cliui/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
+ "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==",
"license": "MIT",
- "engines": {
- "node": ">=8"
+ "dependencies": {
+ "lodash-es": "^4.17.21"
+ },
+ "peerDependencies": {
+ "chevrotain": "^11.0.0"
}
},
- "node_modules/cliui/node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "color-convert": "^2.0.1"
+ "readdirp": "^4.0.1"
},
"engines": {
- "node": ">=8"
+ "node": ">= 14.16.0"
},
"funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ "url": "https://paulmillr.com/funding/"
}
},
- "node_modules/cliui/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/cliui/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "node_modules/chromium-bidi": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz",
+ "integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==",
"dev": true,
- "license": "MIT",
+ "license": "Apache-2.0",
"dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
+ "mitt": "^3.0.1",
+ "zod": "^3.24.1"
},
- "engines": {
- "node": ">=8"
+ "peerDependencies": {
+ "devtools-protocol": "*"
}
},
- "node_modules/cliui/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "node_modules/chromium-bidi/node_modules/zod": {
+ "version": "3.25.76",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz",
+ "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
- "node_modules/cliui/node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
- "license": "MIT",
+ "license": "ISC",
"dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
},
"engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ "node": ">=12"
}
},
"node_modules/clsx": {
@@ -3729,10 +3206,13 @@
}
},
"node_modules/common-ancestor-path": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz",
- "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==",
- "license": "ISC"
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz",
+ "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">= 18"
+ }
},
"node_modules/confbox": {
"version": "0.2.2",
@@ -3779,9 +3259,9 @@
}
},
"node_modules/cosmiconfig": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
- "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz",
+ "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4532,18 +4012,6 @@
"node": ">=8"
}
},
- "node_modules/deterministic-object-hash": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz",
- "integrity": "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==",
- "license": "MIT",
- "dependencies": {
- "base-64": "^1.0.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/devalue": {
"version": "5.6.4",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.4.tgz",
@@ -4564,9 +4032,9 @@
}
},
"node_modules/devtools-protocol": {
- "version": "0.0.1534754",
- "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1534754.tgz",
- "integrity": "sha512-26T91cV5dbOYnXdJi5qQHoTtUoNEqwkHcAyu/IKtjIAxiEqPMrDiRkDOPWVsGfNZGmlQVHQbZRSjD8sxagWVsQ==",
+ "version": "0.0.1581282",
+ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1581282.tgz",
+ "integrity": "sha512-nv7iKtNZQshSW2hKzYNr46nM/Cfh5SEvE2oV0/SEGgc9XupIY5ggf84Cz8eJIkBce7S3bmTAauFD6aysMpnqsQ==",
"dev": true,
"license": "BSD-3-Clause"
},
@@ -4702,9 +4170,10 @@
}
},
"node_modules/emoji-regex": {
- "version": "10.5.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz",
- "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==",
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
"license": "MIT"
},
"node_modules/end-of-stream": {
@@ -4761,13 +4230,6 @@
"is-arrayish": "^0.2.1"
}
},
- "node_modules/error-ex/node_modules/is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
@@ -4789,9 +4251,9 @@
}
},
"node_modules/es-module-lexer": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
- "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
+ "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
"license": "MIT"
},
"node_modules/es-object-atoms": {
@@ -4840,9 +4302,9 @@
}
},
"node_modules/esbuild": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
- "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
+ "version": "0.27.4",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz",
+ "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
@@ -4852,32 +4314,32 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.25.9",
- "@esbuild/android-arm": "0.25.9",
- "@esbuild/android-arm64": "0.25.9",
- "@esbuild/android-x64": "0.25.9",
- "@esbuild/darwin-arm64": "0.25.9",
- "@esbuild/darwin-x64": "0.25.9",
- "@esbuild/freebsd-arm64": "0.25.9",
- "@esbuild/freebsd-x64": "0.25.9",
- "@esbuild/linux-arm": "0.25.9",
- "@esbuild/linux-arm64": "0.25.9",
- "@esbuild/linux-ia32": "0.25.9",
- "@esbuild/linux-loong64": "0.25.9",
- "@esbuild/linux-mips64el": "0.25.9",
- "@esbuild/linux-ppc64": "0.25.9",
- "@esbuild/linux-riscv64": "0.25.9",
- "@esbuild/linux-s390x": "0.25.9",
- "@esbuild/linux-x64": "0.25.9",
- "@esbuild/netbsd-arm64": "0.25.9",
- "@esbuild/netbsd-x64": "0.25.9",
- "@esbuild/openbsd-arm64": "0.25.9",
- "@esbuild/openbsd-x64": "0.25.9",
- "@esbuild/openharmony-arm64": "0.25.9",
- "@esbuild/sunos-x64": "0.25.9",
- "@esbuild/win32-arm64": "0.25.9",
- "@esbuild/win32-ia32": "0.25.9",
- "@esbuild/win32-x64": "0.25.9"
+ "@esbuild/aix-ppc64": "0.27.4",
+ "@esbuild/android-arm": "0.27.4",
+ "@esbuild/android-arm64": "0.27.4",
+ "@esbuild/android-x64": "0.27.4",
+ "@esbuild/darwin-arm64": "0.27.4",
+ "@esbuild/darwin-x64": "0.27.4",
+ "@esbuild/freebsd-arm64": "0.27.4",
+ "@esbuild/freebsd-x64": "0.27.4",
+ "@esbuild/linux-arm": "0.27.4",
+ "@esbuild/linux-arm64": "0.27.4",
+ "@esbuild/linux-ia32": "0.27.4",
+ "@esbuild/linux-loong64": "0.27.4",
+ "@esbuild/linux-mips64el": "0.27.4",
+ "@esbuild/linux-ppc64": "0.27.4",
+ "@esbuild/linux-riscv64": "0.27.4",
+ "@esbuild/linux-s390x": "0.27.4",
+ "@esbuild/linux-x64": "0.27.4",
+ "@esbuild/netbsd-arm64": "0.27.4",
+ "@esbuild/netbsd-x64": "0.27.4",
+ "@esbuild/openbsd-arm64": "0.27.4",
+ "@esbuild/openbsd-x64": "0.27.4",
+ "@esbuild/openharmony-arm64": "0.27.4",
+ "@esbuild/sunos-x64": "0.27.4",
+ "@esbuild/win32-arm64": "0.27.4",
+ "@esbuild/win32-ia32": "0.27.4",
+ "@esbuild/win32-x64": "0.27.4"
}
},
"node_modules/escalade": {
@@ -5078,9 +4540,9 @@
}
},
"node_modules/eventemitter3": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
- "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
"license": "MIT"
},
"node_modules/events-universal": {
@@ -5094,15 +4556,15 @@
}
},
"node_modules/expressive-code": {
- "version": "0.41.3",
- "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.41.3.tgz",
- "integrity": "sha512-YLnD62jfgBZYrXIPQcJ0a51Afv9h8VlWqEGK9uU2T5nL/5rb8SnA86+7+mgCZe5D34Tff5RNEA5hjNVJYHzrFg==",
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.41.7.tgz",
+ "integrity": "sha512-2wZjC8OQ3TaVEMcBtYY4Va3lo6J+Ai9jf3d4dbhURMJcU4Pbqe6EcHe424MIZI0VHUA1bR6xdpoHYi3yxokWqA==",
"license": "MIT",
"dependencies": {
- "@expressive-code/core": "^0.41.3",
- "@expressive-code/plugin-frames": "^0.41.3",
- "@expressive-code/plugin-shiki": "^0.41.3",
- "@expressive-code/plugin-text-markers": "^0.41.3"
+ "@expressive-code/core": "^0.41.7",
+ "@expressive-code/plugin-frames": "^0.41.7",
+ "@expressive-code/plugin-shiki": "^0.41.7",
+ "@expressive-code/plugin-text-markers": "^0.41.7"
}
},
"node_modules/exsolve": {
@@ -5146,16 +4608,19 @@
"license": "MIT"
},
"node_modules/fast-xml-builder": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz",
- "integrity": "sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz",
+ "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
- "license": "MIT"
+ "license": "MIT",
+ "dependencies": {
+ "path-expression-matcher": "^1.1.3"
+ }
},
"node_modules/fast-xml-parser": {
"version": "5.4.1",
@@ -5246,18 +4711,18 @@
}
},
"node_modules/fontace": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.0.tgz",
- "integrity": "sha512-moThBCItUe2bjZip5PF/iZClpKHGLwMvR79Kp8XpGRBrvoRSnySN4VcILdv3/MJzbhvUA5WeiUXF5o538m5fvg==",
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz",
+ "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==",
"license": "MIT",
"dependencies": {
- "fontkitten": "^1.0.0"
+ "fontkitten": "^1.0.2"
}
},
"node_modules/fontkitten": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.0.tgz",
- "integrity": "sha512-b0RdzQeztiiUFWEDzq6Ka26qkNVNLCehoRtifOIGNbQ4CfxyYRh73fyWaQX/JshPVcueITOEeoSWPy5XQv8FUg==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz",
+ "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==",
"license": "MIT",
"dependencies": {
"tiny-inflate": "^1.0.3"
@@ -5300,18 +4765,6 @@
"node": "6.* || 8.* || >= 10.*"
}
},
- "node_modules/get-east-asian-width": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz",
- "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -5786,15 +5239,15 @@
}
},
"node_modules/hast-util-to-parse5": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz",
- "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==",
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz",
+ "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0",
"comma-separated-tokens": "^2.0.0",
"devlop": "^1.0.0",
- "property-information": "^6.0.0",
+ "property-information": "^7.0.0",
"space-separated-tokens": "^2.0.0",
"web-namespaces": "^2.0.0",
"zwitch": "^2.0.0"
@@ -5804,16 +5257,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/hast-util-to-parse5/node_modules/property-information": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
- "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
"node_modules/hast-util-to-string": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz",
@@ -5939,16 +5382,17 @@
"license": "BSD-2-Clause"
},
"node_modules/http-errors": {
- "version": "1.6.3",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
- "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
+ "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"depd": "~1.1.2",
- "inherits": "2.0.3",
- "setprototypeof": "1.1.0",
- "statuses": ">= 1.4.0 < 2"
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.6"
@@ -6018,22 +5462,6 @@
"node": ">=12"
}
},
- "node_modules/http-server/node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
"node_modules/http-server/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -6128,16 +5556,16 @@
}
},
"node_modules/inherits": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true,
"license": "ISC"
},
"node_modules/inline-style-parser": {
- "version": "0.2.4",
- "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz",
- "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==",
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
+ "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",
"license": "MIT"
},
"node_modules/internmap": {
@@ -6169,12 +5597,12 @@
}
},
"node_modules/is-absolute-url": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-4.0.1.tgz",
- "integrity": "sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-5.0.0.tgz",
+ "integrity": "sha512-sdJyNpBnQHuVnBunfzjAecOhZr2+A30ywfFvu3EnxtKLUWfwGgyWUmqHbGZiU6vTfHpCPm5GvLe4BAvlU9n8VQ==",
"license": "MIT",
"engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ "node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -6204,6 +5632,13 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/is-decimal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
@@ -6233,6 +5668,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -6329,9 +5765,9 @@
"license": "MIT"
},
"node_modules/katex": {
- "version": "0.16.25",
- "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.25.tgz",
- "integrity": "sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==",
+ "version": "0.16.38",
+ "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.38.tgz",
+ "integrity": "sha512-cjHooZUmIAUmDsHBN+1n8LaZdpmbj03LtYeYPyuYB7OuloiaeaV6N4LcfjcnHVzGWjVQmKrxxTrpDcmSzEZQwQ==",
"funding": [
"https://opencollective.com/katex",
"https://github.com/sponsors/katex"
@@ -6481,13 +5917,13 @@
}
},
"node_modules/magicast": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.1.tgz",
- "integrity": "sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==",
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz",
+ "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.28.5",
- "@babel/types": "^7.28.5",
+ "@babel/parser": "^7.29.0",
+ "@babel/types": "^7.29.0",
"source-map-js": "^1.2.1"
}
},
@@ -6504,9 +5940,9 @@
}
},
"node_modules/markdown-it": {
- "version": "14.1.0",
- "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
- "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
+ "version": "14.1.1",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz",
+ "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6670,9 +6106,9 @@
}
},
"node_modules/mdast-util-from-markdown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
- "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz",
+ "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
@@ -7969,6 +7405,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/obug": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
+ "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
+ "funding": [
+ "https://github.com/sponsors/sxzz",
+ "https://opencollective.com/debug"
+ ],
+ "license": "MIT"
+ },
"node_modules/ofetch": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz",
@@ -8003,13 +7449,13 @@
"license": "MIT"
},
"node_modules/oniguruma-to-es": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.4.tgz",
- "integrity": "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==",
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.5.tgz",
+ "integrity": "sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==",
"license": "MIT",
"dependencies": {
"oniguruma-parser": "^0.12.1",
- "regex": "^6.0.1",
+ "regex": "^6.1.0",
"regex-recursion": "^6.0.2"
}
},
@@ -8024,43 +7470,43 @@
}
},
"node_modules/p-limit": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz",
- "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==",
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.0.tgz",
+ "integrity": "sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw==",
"license": "MIT",
"dependencies": {
- "yocto-queue": "^1.1.1"
+ "yocto-queue": "^1.2.1"
},
"engines": {
- "node": ">=18"
+ "node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-queue": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.1.tgz",
- "integrity": "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==",
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.1.0.tgz",
+ "integrity": "sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==",
"license": "MIT",
"dependencies": {
"eventemitter3": "^5.0.1",
- "p-timeout": "^6.1.2"
+ "p-timeout": "^7.0.0"
},
"engines": {
- "node": ">=18"
+ "node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-timeout": {
- "version": "6.1.4",
- "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz",
- "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz",
+ "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==",
"license": "MIT",
"engines": {
- "node": ">=14.16"
+ "node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -8226,6 +7672,21 @@
"integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==",
"license": "MIT"
},
+ "node_modules/path-expression-matcher": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz",
+ "integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
@@ -8352,9 +7813,9 @@
}
},
"node_modules/postcss": {
- "version": "8.5.6",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
- "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "version": "8.5.8",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
+ "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
"funding": [
{
"type": "opencollective",
@@ -8395,70 +7856,32 @@
],
"license": "MIT",
"dependencies": {
- "postcss-selector-parser": "^6.1.1"
- },
- "engines": {
- "node": ">=12.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.14"
- }
- },
- "node_modules/postcss-nesting": {
- "version": "13.0.2",
- "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz",
- "integrity": "sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/csstools"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/csstools"
- }
- ],
- "license": "MIT-0",
- "dependencies": {
- "@csstools/selector-resolve-nested": "^3.1.0",
- "@csstools/selector-specificity": "^5.0.0",
- "postcss-selector-parser": "^7.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "postcss": "^8.4"
- }
- },
- "node_modules/postcss-nesting/node_modules/@csstools/selector-resolve-nested": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz",
- "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/csstools"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/csstools"
- }
- ],
- "license": "MIT-0",
+ "postcss-selector-parser": "^6.1.1"
+ },
"engines": {
- "node": ">=18"
+ "node": ">=12.0"
},
"peerDependencies": {
- "postcss-selector-parser": "^7.0.0"
+ "postcss": "^8.2.14"
}
},
- "node_modules/postcss-nesting/node_modules/@csstools/selector-specificity": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz",
- "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==",
+ "node_modules/postcss-nested/node_modules/postcss-selector-parser": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-nesting": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz",
+ "integrity": "sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==",
"dev": true,
"funding": [
{
@@ -8471,14 +7894,19 @@
}
],
"license": "MIT-0",
+ "dependencies": {
+ "@csstools/selector-resolve-nested": "^3.1.0",
+ "@csstools/selector-specificity": "^5.0.0",
+ "postcss-selector-parser": "^7.0.0"
+ },
"engines": {
"node": ">=18"
},
"peerDependencies": {
- "postcss-selector-parser": "^7.0.0"
+ "postcss": "^8.4"
}
},
- "node_modules/postcss-nesting/node_modules/postcss-selector-parser": {
+ "node_modules/postcss-selector-parser": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
"integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
@@ -8492,19 +7920,6 @@
"node": ">=4"
}
},
- "node_modules/postcss-selector-parser": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
- "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
- "license": "MIT",
- "dependencies": {
- "cssesc": "^3.0.0",
- "util-deprecate": "^1.0.2"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/prismjs": {
"version": "1.30.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
@@ -8524,28 +7939,6 @@
"node": ">=0.4.0"
}
},
- "node_modules/prompts": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
- "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
- "license": "MIT",
- "dependencies": {
- "kleur": "^3.0.3",
- "sisteransi": "^1.0.5"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/prompts/node_modules/kleur": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
- "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/property-information": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz",
@@ -8594,9 +7987,9 @@
"license": "MIT"
},
"node_modules/pump": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
- "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
+ "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8615,19 +8008,19 @@
}
},
"node_modules/puppeteer-core": {
- "version": "24.33.0",
- "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.33.0.tgz",
- "integrity": "sha512-tPTxVg+Qdj/8av4cy6szv3GlhxeOoNhiiMZ955fjxQyvPQE/6DjCa6ZyF/x0WJrlgBZtaLSP8TQgJb7FdLDXXA==",
+ "version": "24.39.1",
+ "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.39.1.tgz",
+ "integrity": "sha512-AMqQIKoEhPS6CilDzw0Gd1brLri3emkC+1N2J6ZCCuY1Cglo56M63S0jOeBZDQlemOiRd686MYVMl9ELJBzN3A==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@puppeteer/browsers": "2.11.0",
- "chromium-bidi": "11.0.0",
+ "@puppeteer/browsers": "2.13.0",
+ "chromium-bidi": "14.0.0",
"debug": "^4.4.3",
- "devtools-protocol": "0.0.1534754",
- "typed-query-selector": "^2.12.0",
- "webdriver-bidi-protocol": "0.3.9",
- "ws": "^8.18.3"
+ "devtools-protocol": "0.0.1581282",
+ "typed-query-selector": "^2.12.1",
+ "webdriver-bidi-protocol": "0.4.1",
+ "ws": "^8.19.0"
},
"engines": {
"node": ">=18"
@@ -8793,12 +8186,12 @@
}
},
"node_modules/rehype-expressive-code": {
- "version": "0.41.3",
- "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.41.3.tgz",
- "integrity": "sha512-8d9Py4c/V6I/Od2VIXFAdpiO2kc0SV2qTJsRAaqSIcM9aruW4ASLNe2kOEo1inXAAkIhpFzAHTc358HKbvpNUg==",
+ "version": "0.41.7",
+ "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.41.7.tgz",
+ "integrity": "sha512-25f8ZMSF1d9CMscX7Cft0TSQIqdwjce2gDOvQ+d/w0FovsMwrSt3ODP4P3Z7wO1jsIJ4eYyaDRnIR/27bd/EMQ==",
"license": "MIT",
"dependencies": {
- "expressive-code": "^0.41.3"
+ "expressive-code": "^0.41.7"
}
},
"node_modules/rehype-format": {
@@ -9203,9 +8596,9 @@
"license": "MIT"
},
"node_modules/semver": {
- "version": "7.7.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
- "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -9215,22 +8608,26 @@
}
},
"node_modules/serve-index": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
- "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.2.tgz",
+ "integrity": "sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "accepts": "~1.3.4",
+ "accepts": "~1.3.8",
"batch": "0.6.1",
"debug": "2.6.9",
"escape-html": "~1.0.3",
- "http-errors": "~1.6.2",
- "mime-types": "~2.1.17",
- "parseurl": "~1.3.2"
+ "http-errors": "~1.8.0",
+ "mime-types": "~2.1.35",
+ "parseurl": "~1.3.3"
},
"engines": {
"node": ">= 0.8.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/serve-index/node_modules/debug": {
@@ -9251,9 +8648,9 @@
"license": "MIT"
},
"node_modules/setprototypeof": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
- "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"dev": true,
"license": "ISC"
},
@@ -9511,19 +8908,22 @@
}
},
"node_modules/shiki": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.21.0.tgz",
- "integrity": "sha512-N65B/3bqL/TI2crrXr+4UivctrAGEjmsib5rPMMPpFp1xAx/w03v8WZ9RDDFYteXoEgY7qZ4HGgl5KBIu1153w==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.0.2.tgz",
+ "integrity": "sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==",
"license": "MIT",
"dependencies": {
- "@shikijs/core": "3.21.0",
- "@shikijs/engine-javascript": "3.21.0",
- "@shikijs/engine-oniguruma": "3.21.0",
- "@shikijs/langs": "3.21.0",
- "@shikijs/themes": "3.21.0",
- "@shikijs/types": "3.21.0",
+ "@shikijs/core": "4.0.2",
+ "@shikijs/engine-javascript": "4.0.2",
+ "@shikijs/engine-oniguruma": "4.0.2",
+ "@shikijs/langs": "4.0.2",
+ "@shikijs/themes": "4.0.2",
+ "@shikijs/types": "4.0.2",
"@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=20"
}
},
"node_modules/side-channel": {
@@ -9609,28 +9009,37 @@
"license": "MIT"
},
"node_modules/sitemap": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-8.0.0.tgz",
- "integrity": "sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A==",
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz",
+ "integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==",
"license": "MIT",
"dependencies": {
- "@types/node": "^17.0.5",
+ "@types/node": "^24.9.2",
"@types/sax": "^1.2.1",
"arg": "^5.0.0",
- "sax": "^1.2.4"
+ "sax": "^1.4.1"
},
"bin": {
- "sitemap": "dist/cli.js"
+ "sitemap": "dist/esm/cli.js"
},
"engines": {
- "node": ">=14.0.0",
- "npm": ">=6.0.0"
+ "node": ">=20.19.5",
+ "npm": ">=10.8.2"
}
},
"node_modules/sitemap/node_modules/@types/node": {
- "version": "17.0.45",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
- "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==",
+ "version": "24.12.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz",
+ "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/sitemap/node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"license": "MIT"
},
"node_modules/smart-buffer": {
@@ -9750,114 +9159,114 @@
}
},
"node_modules/starlight-blog": {
- "version": "0.25.3",
- "resolved": "https://registry.npmjs.org/starlight-blog/-/starlight-blog-0.25.3.tgz",
- "integrity": "sha512-U0+CYgeJhbXz/6Rcr2vXCUiMq5HyQUq9gP0Nj3Ii2A8GKaDoZTdgHD2HnWJBK4NbXRCJqM7N1BcyeLHS0+O7Aw==",
+ "version": "0.26.1",
+ "resolved": "https://registry.npmjs.org/starlight-blog/-/starlight-blog-0.26.1.tgz",
+ "integrity": "sha512-c2aLtkVTNFKHDJrh1DwlxBj+VyOrLqJeHmGkKKHwL6oowUoaqUiC/vyOZLXNmZL1kcYI0hgHDw1t4G/lkg8mZg==",
"license": "MIT",
"dependencies": {
- "@astrojs/markdown-remark": "^6.3.1",
- "@astrojs/mdx": "^4.0.8",
- "@astrojs/rss": "^4.0.11",
+ "@astrojs/markdown-remark": "^7.0.0",
+ "@astrojs/mdx": "^5.0.0",
+ "@astrojs/rss": "^4.0.17",
"astro-remote": "^0.3.4",
"github-slugger": "^2.0.0",
"hast-util-from-html": "^2.0.3",
"hast-util-to-html": "^9.0.5",
"hast-util-to-string": "^3.0.1",
- "marked": "^15.0.4",
+ "marked": "^17.0.4",
"marked-plaintify": "^1.1.1",
"mdast-util-mdx-expression": "^2.0.1",
- "unist-util-is": "^6.0.0",
+ "unist-util-is": "^6.0.1",
"unist-util-remove": "^4.0.0",
- "unist-util-visit": "^5.0.0"
+ "unist-util-visit": "^5.1.0"
},
"engines": {
- "node": ">=18.20.8"
+ "node": ">=22.12.0"
},
"peerDependencies": {
- "@astrojs/starlight": ">=0.33.0"
+ "@astrojs/starlight": ">=0.38.0"
}
},
"node_modules/starlight-blog/node_modules/marked": {
- "version": "15.0.12",
- "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz",
- "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
+ "version": "17.0.4",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.4.tgz",
+ "integrity": "sha512-NOmVMM+KAokHMvjWmC5N/ZOvgmSWuqJB8FoYI019j4ogb/PeRMKoKIjReZ2w3376kkA8dSJIP8uD993Kxc0iRQ==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
- "node": ">= 18"
+ "node": ">= 20"
}
},
"node_modules/starlight-changelogs": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/starlight-changelogs/-/starlight-changelogs-0.4.0.tgz",
- "integrity": "sha512-Yzd10aAet3flycj5dZoUWryE7jdzwhkmoQpHScYUqxF93pE09GCXGGWsfGA/TnG3VH9RquSEBAVwRWz7hyT6Ww==",
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/starlight-changelogs/-/starlight-changelogs-0.5.0.tgz",
+ "integrity": "sha512-bqBAHle6N4jApkLz8qlN+k8nmaJUXvYunrJeYWTZats15SeEmsT+1N7KdshODCky2AzLfIBy4Qa2v1YSQNrjxQ==",
"license": "MIT",
"dependencies": {
"@ascorbic/loader-utils": "^1.0.2",
"github-slugger": "^2.0.0",
- "mdast-util-from-markdown": "^2.0.2",
+ "mdast-util-from-markdown": "^2.0.3",
"mdast-util-to-markdown": "^2.1.2",
"mdast-util-to-string": "^4.0.0",
- "unist-util-visit": "^5.0.0"
+ "unist-util-visit": "^5.1.0"
},
"engines": {
- "node": ">=18.17.1"
+ "node": ">=22.12.0"
},
"peerDependencies": {
- "@astrojs/starlight": ">=0.35.0"
+ "@astrojs/starlight": ">=0.38.0"
}
},
"node_modules/starlight-github-alerts": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/starlight-github-alerts/-/starlight-github-alerts-0.1.1.tgz",
- "integrity": "sha512-HzVp24sO2VoqJ65H58rh/HRBBc/DGUE5tdQYi3kkckdRHLZJUIkBa7EMF3dyiFw1rTRAzSN5t8r9riijnNvIzA==",
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/starlight-github-alerts/-/starlight-github-alerts-0.2.0.tgz",
+ "integrity": "sha512-tba3q9S+Oc63McFBjvlu0gNDL1YkeaoPFVex3iD9XAvBmp0J63ynMj/QDCcTfsBNPdFD+MIdsNi18It+aZwn4A==",
"dev": true,
"license": "MIT",
"dependencies": {
- "unist-util-visit": "^5.0.0"
+ "unist-util-visit": "^5.1.0"
},
"engines": {
- "node": ">=18.17.1"
+ "node": ">=22.12.0"
},
"peerDependencies": {
- "@astrojs/starlight": ">=0.35.0"
+ "@astrojs/starlight": ">=0.38.0"
}
},
"node_modules/starlight-links-validator": {
- "version": "0.19.2",
- "resolved": "https://registry.npmjs.org/starlight-links-validator/-/starlight-links-validator-0.19.2.tgz",
- "integrity": "sha512-IHeK3R78fsmv53VfRkGbXkwK1CQEUBHM9QPzBEyoAxjZ/ssi5gjV+F4oNNUppTR48iPp+lEY0MTAmvkX7yNnkw==",
+ "version": "0.20.1",
+ "resolved": "https://registry.npmjs.org/starlight-links-validator/-/starlight-links-validator-0.20.1.tgz",
+ "integrity": "sha512-bPfX9tlvkLcygIwo4nszOf7TOj1iqfZEpUss5oxRlaa3/mt7zGBHANSkanVMd0FK6VQa2oUD9MjH1DszykkwPA==",
"license": "MIT",
"dependencies": {
- "@types/picomatch": "^3.0.1",
+ "@types/picomatch": "^4.0.2",
"github-slugger": "^2.0.0",
"hast-util-from-html": "^2.0.3",
"hast-util-has-property": "^3.0.0",
- "is-absolute-url": "^4.0.1",
+ "is-absolute-url": "^5.0.0",
"kleur": "^4.1.5",
- "mdast-util-mdx-jsx": "^3.1.3",
+ "mdast-util-mdx-jsx": "^3.2.0",
"mdast-util-to-string": "^4.0.0",
- "picomatch": "^4.0.2",
+ "picomatch": "^4.0.3",
"terminal-link": "^5.0.0",
- "unist-util-visit": "^5.0.0"
+ "unist-util-visit": "^5.1.0"
},
"engines": {
- "node": ">=18.17.1"
+ "node": ">=22.12.0"
},
"peerDependencies": {
- "@astrojs/starlight": ">=0.32.0",
- "astro": ">=5.1.5"
+ "@astrojs/starlight": ">=0.38.0",
+ "astro": ">=6.0.0"
}
},
"node_modules/starlight-llms-txt": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/starlight-llms-txt/-/starlight-llms-txt-0.7.0.tgz",
- "integrity": "sha512-KAay6JLXqB0GiNQ481z3Z/h/y4xeAU55TUGLz+npjxcRvN3h/7rDxjmyLiphZF8xfoqqSTduQPanl5Ct4Je6kA==",
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/starlight-llms-txt/-/starlight-llms-txt-0.8.0.tgz",
+ "integrity": "sha512-SyENqKy5SMms0/RsEPLv1wLI3EvqZeVgVKQZ6umBR4PXEiIg9iisxXPD3lXOdoxEr+ro4Z/9z731UVOgz8LGKA==",
"license": "MIT",
"dependencies": {
- "@astrojs/mdx": "^4.3.13",
+ "@astrojs/mdx": "^5.0.0",
"@types/hast": "^3.0.4",
"@types/micromatch": "^4.0.10",
"github-slugger": "^2.0.0",
@@ -9870,12 +9279,9 @@
"unified": "^11.0.5",
"unist-util-remove": "^4.0.0"
},
- "engines": {
- "node": "^18.17.1 || ^20.3.0 || >=21.0.0"
- },
"peerDependencies": {
- "@astrojs/starlight": ">=0.31",
- "astro": "^5.15.9"
+ "@astrojs/starlight": ">=0.38.0",
+ "astro": "^6.0.0"
}
},
"node_modules/statuses": {
@@ -9907,20 +9313,18 @@
}
},
"node_modules/string-width": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
- "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "emoji-regex": "^10.3.0",
- "get-east-asian-width": "^1.0.0",
- "strip-ansi": "^7.1.0"
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
},
"engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": ">=8"
}
},
"node_modules/stringify-entities": {
@@ -9938,24 +9342,22 @@
}
},
"node_modules/strip-ansi": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
- "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "ansi-regex": "^6.0.1"
+ "ansi-regex": "^5.0.1"
},
"engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ "node": ">=8"
}
},
"node_modules/strnum": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz",
- "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz",
+ "integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==",
"funding": [
{
"type": "github",
@@ -9965,21 +9367,21 @@
"license": "MIT"
},
"node_modules/style-to-js": {
- "version": "1.1.17",
- "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz",
- "integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==",
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
+ "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==",
"license": "MIT",
"dependencies": {
- "style-to-object": "1.0.9"
+ "style-to-object": "1.0.14"
}
},
"node_modules/style-to-object": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz",
- "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==",
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz",
+ "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==",
"license": "MIT",
"dependencies": {
- "inline-style-parser": "0.2.4"
+ "inline-style-parser": "0.2.7"
}
},
"node_modules/stylis": {
@@ -10076,9 +9478,9 @@
}
},
"node_modules/tar-fs": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz",
- "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz",
+ "integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10091,17 +9493,28 @@
}
},
"node_modules/tar-stream": {
- "version": "3.1.7",
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
- "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz",
+ "integrity": "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"b4a": "^1.6.4",
+ "bare-fs": "^4.5.5",
"fast-fifo": "^1.2.0",
"streamx": "^2.15.0"
}
},
+ "node_modules/teex": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz",
+ "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "streamx": "^2.12.5"
+ }
+ },
"node_modules/terminal-link": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-5.0.0.tgz",
@@ -10119,9 +9532,9 @@
}
},
"node_modules/text-decoder": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz",
- "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==",
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz",
+ "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -10134,6 +9547,15 @@
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
"license": "MIT"
},
+ "node_modules/tinyclip": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.12.tgz",
+ "integrity": "sha512-Ae3OVUqifDw0wBriIBS7yVaW44Dp6eSHQcyq4Igc7eN2TJH/2YsicswaW+J/OuMvhpDPOKEgpAZCjkb4hpoyeA==",
+ "license": "MIT",
+ "engines": {
+ "node": "^16.14.0 || >= 17.3.0"
+ }
+ },
"node_modules/tinyexec": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
@@ -10181,6 +9603,16 @@
"node": ">=8.0"
}
},
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
"node_modules/trim-lines": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
@@ -10247,39 +9679,13 @@
"devOptional": true,
"license": "0BSD"
},
- "node_modules/type-fest": {
- "version": "4.41.0",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
- "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
- "license": "(MIT OR CC0-1.0)",
- "engines": {
- "node": ">=16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/typed-query-selector": {
- "version": "2.12.0",
- "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz",
- "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==",
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.1.tgz",
+ "integrity": "sha512-uzR+FzI8qrUEIu96oaeBJmd9E7CFEiQ3goA5qCVgc4s5llSubcfGHq9yUstZx/k4s9dXHVKsE35YWoFyvEqEHA==",
"dev": true,
"license": "MIT"
},
- "node_modules/typescript": {
- "version": "5.9.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
- "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
- "license": "Apache-2.0",
- "peer": true,
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
"node_modules/uc.micro": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
@@ -10306,9 +9712,9 @@
"license": "MIT"
},
"node_modules/undici-types": {
- "version": "7.10.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
- "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
+ "version": "7.18.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
+ "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"license": "MIT"
},
"node_modules/unified": {
@@ -10331,9 +9737,9 @@
}
},
"node_modules/unifont": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.3.tgz",
- "integrity": "sha512-b0GtQzKCyuSHGsfj5vyN8st7muZ6VCI4XD4vFlr7Uy1rlWVYxC3npnfk8MyreHxJYrz1ooLDqDzFe9XqQTlAhA==",
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.4.tgz",
+ "integrity": "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==",
"license": "MIT",
"dependencies": {
"css-tree": "^3.1.0",
@@ -10368,9 +9774,9 @@
}
},
"node_modules/unist-util-is": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
- "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
+ "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0"
@@ -10463,9 +9869,9 @@
}
},
"node_modules/unist-util-visit": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
- "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz",
+ "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0",
@@ -10697,23 +10103,23 @@
}
},
"node_modules/vite": {
- "version": "6.4.1",
- "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
- "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
+ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"license": "MIT",
"dependencies": {
- "esbuild": "^0.25.0",
- "fdir": "^6.4.4",
- "picomatch": "^4.0.2",
- "postcss": "^8.5.3",
- "rollup": "^4.34.9",
- "tinyglobby": "^0.2.13"
+ "esbuild": "^0.27.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
- "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
@@ -10722,14 +10128,14 @@
"fsevents": "~2.3.3"
},
"peerDependencies": {
- "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "@types/node": "^20.19.0 || >=22.12.0",
"jiti": ">=1.21.0",
- "less": "*",
+ "less": "^4.0.0",
"lightningcss": "^1.21.0",
- "sass": "*",
- "sass-embedded": "*",
- "stylus": "*",
- "sugarss": "*",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
"terser": "^5.16.0",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
@@ -10771,9 +10177,9 @@
}
},
"node_modules/vitefu": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz",
- "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.2.tgz",
+ "integrity": "sha512-zpKATdUbzbsycPFBN71nS2uzBUQiVnFoOrr2rvqv34S1lcAgMKKkjWleLGeiJlZ8lwCXvtWaRn7R3ZC16SYRuw==",
"license": "MIT",
"workspaces": [
"tests/deps/*",
@@ -10781,7 +10187,7 @@
"tests/projects/workspace/packages/*"
],
"peerDependencies": {
- "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0"
+ "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0"
},
"peerDependenciesMeta": {
"vite": {
@@ -10849,9 +10255,9 @@
}
},
"node_modules/webdriver-bidi-protocol": {
- "version": "0.3.9",
- "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.9.tgz",
- "integrity": "sha512-uIYvlRQ0PwtZR1EzHlTMol1G0lAlmOe6wPykF9a77AK3bkpvZHzIVxRE2ThOx5vjy2zISe0zhwf5rzuUfbo1PQ==",
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz",
+ "integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==",
"dev": true,
"license": "Apache-2.0"
},
@@ -10885,33 +10291,19 @@
"dev": true,
"license": "MIT"
},
- "node_modules/widest-line": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz",
- "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==",
- "license": "MIT",
- "dependencies": {
- "string-width": "^7.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/wrap-ansi": {
- "version": "9.0.2",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
- "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "ansi-styles": "^6.2.1",
- "string-width": "^7.0.0",
- "strip-ansi": "^7.1.0"
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
},
"engines": {
- "node": ">=18"
+ "node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
@@ -10925,9 +10317,9 @@
"license": "ISC"
},
"node_modules/ws": {
- "version": "8.18.3",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
- "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
+ "version": "8.19.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
+ "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -11021,57 +10413,22 @@
}
},
"node_modules/yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+ "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
"license": "ISC",
"engines": {
- "node": ">=12"
- }
- },
- "node_modules/yargs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/yargs/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/yargs/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
+ "node": "^20.19.0 || ^22.12.0 || >=23"
}
},
- "node_modules/yargs/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "node_modules/yargs/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
+ "license": "ISC",
"engines": {
- "node": ">=8"
+ "node": ">=12"
}
},
"node_modules/yauzl": {
@@ -11086,9 +10443,9 @@
}
},
"node_modules/yocto-queue": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz",
- "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==",
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz",
+ "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==",
"license": "MIT",
"engines": {
"node": ">=12.20"
@@ -11097,60 +10454,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/yocto-spinner": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-0.2.3.tgz",
- "integrity": "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==",
- "license": "MIT",
- "dependencies": {
- "yoctocolors": "^2.1.1"
- },
- "engines": {
- "node": ">=18.19"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/yoctocolors": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz",
- "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/zod": {
- "version": "3.25.76",
- "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
- "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
+ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
- "node_modules/zod-to-json-schema": {
- "version": "3.25.1",
- "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz",
- "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==",
- "license": "ISC",
- "peerDependencies": {
- "zod": "^3.25 || ^4"
- }
- },
- "node_modules/zod-to-ts": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz",
- "integrity": "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==",
- "peerDependencies": {
- "typescript": "^4.9.4 || ^5.0.2",
- "zod": "^3"
- }
- },
"node_modules/zwitch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
diff --git a/docs/package.json b/docs/package.json
index 2032178cbbc..c388e04b87d 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -17,23 +17,23 @@
"test:debug": "playwright test --debug"
},
"dependencies": {
- "@astrojs/starlight": "^0.37.7",
+ "@astrojs/starlight": "^0.38.1",
"@primer/octicons": "^19.22.0",
- "astro": "^5.18.0",
+ "astro": "^6.0.5",
"astro-mermaid": "^1.3.1",
"mermaid": "^11.13.0",
"sharp": "^0.34.5",
- "starlight-blog": "^0.25.3",
- "starlight-changelogs": "^0.4.0",
- "starlight-links-validator": "^0.19.2",
- "starlight-llms-txt": "^0.7.0",
+ "starlight-blog": "^0.26.1",
+ "starlight-changelogs": "^0.5.0",
+ "starlight-links-validator": "^0.20.1",
+ "starlight-llms-txt": "^0.8.0",
"yaml": "^2.8.2"
},
"devDependencies": {
- "@marp-team/marp-cli": "^4.2.3",
+ "@marp-team/marp-cli": "^4.3.1",
"@playwright/test": "^1.58.2",
"@types/primer__octicons": "^19.21.0",
"http-server": "^14.1.1",
- "starlight-github-alerts": "^0.1.1"
+ "starlight-github-alerts": "^0.2.0"
}
}
diff --git a/docs/src/content/docs/agent-factory-status.mdx b/docs/src/content/docs/agent-factory-status.mdx
index af5e5655ce7..a0bcff388ce 100644
--- a/docs/src/content/docs/agent-factory-status.mdx
+++ b/docs/src/content/docs/agent-factory-status.mdx
@@ -28,7 +28,6 @@ These are experimental agentic workflows used by the GitHub Next team to learn,
| [Brave Web Search Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/brave.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/brave.lock.yml) | - | `/brave` |
| [Breaking Change Checker](https://github.com/github/gh-aw/blob/main/.github/workflows/breaking-change-checker.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/breaking-change-checker.lock.yml) | - | - |
| [Changeset Generator](https://github.com/github/gh-aw/blob/main/.github/workflows/changeset.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/changeset.lock.yml) | - | - |
-| [Chroma Issue Indexer](https://github.com/github/gh-aw/blob/main/.github/workflows/chroma-issue-indexer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/chroma-issue-indexer.lock.yml) | `0 */4 * * *` | - |
| [CI Cleaner](https://github.com/github/gh-aw/blob/main/.github/workflows/hourly-ci-cleaner.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/hourly-ci-cleaner.lock.yml) | `0 6,18 * * *` | - |
| [CI Failure Doctor](https://github.com/github/gh-aw/blob/main/.github/workflows/ci-doctor.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/ci-doctor.lock.yml) | - | - |
| [CI Optimization Coach](https://github.com/github/gh-aw/blob/main/.github/workflows/ci-coach.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/ci-coach.lock.yml) | `0 13 * * 1-5` | - |
@@ -139,7 +138,12 @@ These are experimental agentic workflows used by the GitHub Next team to learn,
| [Semantic Function Refactoring](https://github.com/github/gh-aw/blob/main/.github/workflows/semantic-function-refactor.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/semantic-function-refactor.lock.yml) | - | - |
| [Sergo - Serena Go Expert](https://github.com/github/gh-aw/blob/main/.github/workflows/sergo.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/sergo.lock.yml) | - | - |
| [Slide Deck Maintainer](https://github.com/github/gh-aw/blob/main/.github/workflows/slide-deck-maintainer.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/slide-deck-maintainer.lock.yml) | `0 16 * * 1-5` | - |
-| [Smoke Agent](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-agent.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/smoke-agent.lock.yml) | - | - |
+| [Smoke Agent: all/merged](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-agent-all-merged.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/smoke-agent-all-merged.lock.yml) | - | - |
+| [Smoke Agent: all/none](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-agent-all-none.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/smoke-agent-all-none.lock.yml) | - | - |
+| [Smoke Agent: public/approved](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-agent-public-approved.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/smoke-agent-public-approved.lock.yml) | - | - |
+| [Smoke Agent: public/none](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-agent-public-none.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/smoke-agent-public-none.lock.yml) | - | - |
+| [Smoke Agent: scoped/approved](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-agent-scoped-approved.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/smoke-agent-scoped-approved.lock.yml) | - | - |
+| [Smoke Call Workflow](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-call-workflow.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/smoke-call-workflow.lock.yml) | - | - |
| [Smoke Claude](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-claude.md) | claude | [](https://github.com/github/gh-aw/actions/workflows/smoke-claude.lock.yml) | - | - |
| [Smoke Codex](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-codex.md) | codex | [](https://github.com/github/gh-aw/actions/workflows/smoke-codex.lock.yml) | - | - |
| [Smoke Copilot](https://github.com/github/gh-aw/blob/main/.github/workflows/smoke-copilot.md) | copilot | [](https://github.com/github/gh-aw/actions/workflows/smoke-copilot.lock.yml) | - | - |
diff --git a/docs/src/content/docs/guides/deterministic-agentic-patterns.md b/docs/src/content/docs/guides/deterministic-agentic-patterns.md
index b601defd048..4ab31dc8333 100644
--- a/docs/src/content/docs/guides/deterministic-agentic-patterns.md
+++ b/docs/src/content/docs/guides/deterministic-agentic-patterns.md
@@ -94,28 +94,137 @@ Pass data between jobs via artifacts, job outputs, or environment variables.
## Custom Trigger Filtering
+### Inline Steps (`on.steps:`) — Preferred
+
+Inject deterministic steps directly into the pre-activation job using `on.steps:`. This saves **one workflow job** compared to the multi-job pattern and is the recommended approach for lightweight filtering:
+
```yaml wrap title=".github/workflows/smart-responder.md"
---
on:
issues:
- types: [opened, edited]
+ types: [opened]
+ steps:
+ - id: check
+ env:
+ LABELS: ${{ toJSON(github.event.issue.labels.*.name) }}
+ run: echo "$LABELS" | grep -q '"bug"'
+ # exits 0 (outcome: success) if the label is found, 1 (outcome: failure) if not
engine: copilot
safe-outputs:
add-comment:
-steps:
- - id: filter
- run: |
- if echo "${{ github.event.issue.body }}" | grep -q "urgent"; then
- echo "priority=high" >> "$GITHUB_OUTPUT"
- else
- exit 1
- fi
+if: needs.pre_activation.outputs.check_result == 'success'
---
-# Smart Issue Responder
+# Bug Issue Responder
+
+Triage bug report: "${{ github.event.issue.title }}" and add-comment with a summary of the next steps.
+```
+
+Each step with an `id` gets an auto-wired output `_result` set to `${{ steps..outcome }}` — `success` when the step's exit code is 0, `failure` when non-zero. Gate the workflow by checking `needs.pre_activation.outputs._result == 'success'`.
+
+To pass an explicit value rather than relying on exit codes, set a step output and re-expose it via `jobs.pre-activation.outputs`:
+
+```yaml wrap
+jobs:
+ pre-activation:
+ outputs:
+ has_bug_label: ${{ steps.check.outputs.has_bug_label }}
+
+if: needs.pre_activation.outputs.has_bug_label == 'true'
+```
+
+When `on.steps:` need GitHub API access, use `on.permissions:` to grant the required scopes to the pre-activation job:
+
+```yaml wrap
+on:
+ schedule: every 30m
+ permissions:
+ issues: read
+ steps:
+ - id: search
+ uses: actions/github-script@v8
+ with:
+ script: |
+ const open = await github.rest.issues.listForRepo({ ...context.repo, state: 'open' });
+ core.setOutput('has_work', open.data.length > 0 ? 'true' : 'false');
-Respond to urgent issue: "${{ github.event.issue.title }}" (Priority: ${{ steps.filter.outputs.priority }})
+jobs:
+ pre-activation:
+ outputs:
+ has_work: ${{ steps.search.outputs.has_work }}
+
+if: needs.pre_activation.outputs.has_work == 'true'
+```
+
+See [Pre-Activation Steps](/gh-aw/reference/triggers/#pre-activation-steps-onsteps) and [Pre-Activation Permissions](/gh-aw/reference/triggers/#pre-activation-permissions-onpermissions) for full documentation.
+
+### Multi-Job Pattern — For Complex Cases
+
+Use a separate `jobs:` entry when filtering requires heavy tooling (checkout, compiled tools, multiple runners):
+
+```yaml wrap title=".github/workflows/smart-responder.md"
+---
+on:
+ issues:
+ types: [opened]
+engine: copilot
+safe-outputs:
+ add-comment:
+
+jobs:
+ filter:
+ runs-on: ubuntu-latest
+ outputs:
+ should-run: ${{ steps.check.outputs.result }}
+ steps:
+ - id: check
+ env:
+ LABELS: ${{ toJSON(github.event.issue.labels.*.name) }}
+ run: |
+ if echo "$LABELS" | grep -q '"bug"'; then
+ echo "result=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "result=false" >> "$GITHUB_OUTPUT"
+ fi
+
+if: needs.filter.outputs.should-run == 'true'
+---
+
+# Bug Issue Responder
+
+Triage bug report: "${{ github.event.issue.title }}" and add-comment with a summary of the next steps.
+```
+
+The compiler automatically adds the filter job as a dependency of the activation job, so when the condition is false the workflow run is **skipped** (not failed), keeping the Actions tab clean.
+
+### Simple Context Conditions
+
+For conditions that can be expressed directly with GitHub Actions context, use `if:` without a custom job:
+
+```yaml wrap
+---
+on:
+ pull_request:
+ types: [opened, synchronize]
+engine: copilot
+if: github.event.pull_request.draft == false
+---
+```
+
+### Query-Based Filtering
+
+For conditions based on GitHub search results, use [`skip-if-match:`](/gh-aw/reference/triggers/#skip-if-match-condition-skip-if-match) or [`skip-if-no-match:`](/gh-aw/reference/triggers/#skip-if-no-match-condition-skip-if-no-match) in the `on:` section — these accept standard [GitHub search query syntax](https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests) and are evaluated in the pre-activation job, producing the same skipped-not-failed behaviour:
+
+```yaml wrap
+---
+on:
+ issues:
+ types: [opened]
+ # Skip if a duplicate issue already exists (GitHub search query syntax)
+ skip-if-match: 'is:issue is:open label:duplicate'
+engine: copilot
+---
```
## Post-Processing Pattern
@@ -178,6 +287,8 @@ Reference in prompts: "Analyze issues in `/tmp/gh-aw/agent/issues.json` and PRs
## Related Documentation
+- [Pre-Activation Steps](/gh-aw/reference/triggers/#pre-activation-steps-onsteps) - Inline step injection into the pre-activation job
+- [Pre-Activation Permissions](/gh-aw/reference/triggers/#pre-activation-permissions-onpermissions) - Grant additional scopes for `on.steps:` API calls
- [Custom Safe Outputs](/gh-aw/reference/custom-safe-outputs/) - Custom post-processing jobs
- [Frontmatter Reference](/gh-aw/reference/frontmatter/) - Configuration options
- [Compilation Process](/gh-aw/reference/compilation-process/) - How jobs are orchestrated
diff --git a/docs/src/content/docs/patterns/label-ops.md b/docs/src/content/docs/patterns/label-ops.md
index 4672f65a782..5f2e38e252b 100644
--- a/docs/src/content/docs/patterns/label-ops.md
+++ b/docs/src/content/docs/patterns/label-ops.md
@@ -5,14 +5,95 @@ sidebar:
badge: { text: 'Event-triggered', variant: 'success' }
---
-LabelOps uses GitHub labels as workflow triggers, metadata, and state markers. GitHub Agentic Workflows supports label-based triggers with filtering to activate workflows only for specific label changes while maintaining secure, automated responses.
+LabelOps uses GitHub labels as workflow triggers, metadata, and state markers. GitHub Agentic Workflows supports two distinct approaches to label-based triggers: `label_command` for command-style one-shot activation, and `names:` filtering for persistent label-state awareness.
## When to Use LabelOps
Use LabelOps for priority-based workflows (run checks when `priority: high` is added), stage transitions (trigger actions when moving between workflow states), specialized processing (different workflows for different label categories), and team coordination (automate handoffs between teams using labels).
+## Label Command Trigger
+
+The `label_command` trigger treats a label as a one-shot command: applying the label fires the workflow, and the label is **automatically removed** so it can be re-applied to re-trigger. This is the right choice when you want a label to mean "do this now" rather than "this item has this property."
+
+```aw wrap
+---
+on:
+ label_command: deploy
+permissions:
+ contents: read
+ actions: write
+safe-outputs:
+ add-comment:
+ max: 1
+---
+
+# Deploy Preview
+
+A `deploy` label was applied to this pull request. Build and deploy a preview environment and post the URL as a comment.
+
+The matched label name is available as `${{ needs.activation.outputs.label_command }}` if needed to distinguish between multiple label commands.
+```
+
+After activation the `deploy` label is removed from the pull request, so a reviewer can apply it again to trigger another deployment without any cleanup step.
+
+### Syntax
+
+`label_command` accepts a shorthand string, a map with a single name, or a map with multiple names and an optional `events` restriction:
+
+```yaml
+# Shorthand — fires on issues, pull_request, and discussion
+on: "label-command deploy"
+
+# Map with a single name
+on:
+ label_command: deploy
+
+# Restrict to specific event types
+on:
+ label_command:
+ name: deploy
+ events: [issues, pull_request]
+
+# Multiple label names
+on:
+ label_command:
+ names: [deploy, redeploy]
+ events: [pull_request]
+```
+
+The compiler generates `issues`, `pull_request`, and/or `discussion` events with `types: [labeled]`, filtered to the named labels. It also adds a `workflow_dispatch` trigger with an `item_number` input so you can test the workflow manually without applying a real label.
+
+### Accessing the matched label
+
+The label that triggered the workflow is exposed as an output of the activation job:
+
+```
+${{ needs.activation.outputs.label_command }}
+```
+
+This is useful when a workflow handles multiple label commands and needs to branch on which one was applied.
+
+### Combining with slash commands
+
+`label_command` can be combined with `slash_command:` in the same workflow. The two triggers are OR'd — the workflow activates when either condition is met:
+
+```yaml
+on:
+ slash_command: deploy
+ label_command:
+ name: deploy
+ events: [pull_request]
+```
+
+This lets a workflow be triggered both by a `/deploy` comment and by applying a `deploy` label, sharing the same agent logic.
+
+> [!NOTE]
+> The automatic label removal requires `issues: write` or `pull-requests: write` permission (depending on item type). Add the relevant permission to your frontmatter when using `label_command`.
+
## Label Filtering
+Use `names:` filtering when you want the workflow to run whenever a label is present on an item and the label should remain attached. This is suitable for monitoring label state rather than reacting to a transient command.
+
GitHub Agentic Workflows allows you to filter `labeled` and `unlabeled` events to trigger only for specific label names using the `names` field:
```aw wrap
@@ -42,7 +123,16 @@ Check the issue for:
Respond with a comment outlining next steps and recommended actions.
```
-This workflow activates only when the `bug`, `critical`, or `security` labels are added to an issue, not for other label changes.
+This workflow activates only when the `bug`, `critical`, or `security` labels are added to an issue, not for other label changes. The labels remain on the issue after the workflow runs.
+
+### Choosing between `label_command` and `names:` filtering
+
+| | `label_command` | `names:` filtering |
+|---|---|---|
+| Label lifecycle | Removed automatically after trigger | Stays on the item |
+| Re-triggerable | Yes — reapply the label | Only on the next `labeled` event |
+| Typical use | "Do this now" commands | State-based routing |
+| Supported items | Issues, pull requests, discussions | Issues, pull requests |
### Label Filter Syntax
diff --git a/docs/src/content/docs/patterns/multi-repo-ops.md b/docs/src/content/docs/patterns/multi-repo-ops.md
index ecba4d9212b..53bbae87ac0 100644
--- a/docs/src/content/docs/patterns/multi-repo-ops.md
+++ b/docs/src/content/docs/patterns/multi-repo-ops.md
@@ -46,8 +46,6 @@ Analyze the issue and create a tracking issue that:
- Provides context for cross-component coordination
```
-This workflow creates a hub-and-spoke architecture where component repositories automatically report issues to a central tracking repository.
-
## Authentication for Cross-Repo Access
Cross-repository operations require authentication beyond the default `GITHUB_TOKEN`, which is scoped to the current repository only.
@@ -147,12 +145,6 @@ tools:
> [!IMPORTANT]
> When reading from repositories other than the workflow's repository, you must configure additional authentication. The default `GITHUB_TOKEN` only has access to the current repository. Use a PAT, GitHub App token, or the magic secret `GH_AW_GITHUB_MCP_SERVER_TOKEN`. See [GitHub Tools Reference](/gh-aw/reference/github-tools/#cross-repository-reading) for details.
-**Available Operations:**
-- **repos**: Read files, search code, list commits, get releases
-- **issues**: List and search issues across repositories
-- **pull_requests**: List and search PRs across repositories
-- **actions**: Access workflow runs and artifacts
-
Agent instructions can reference remote repositories:
```markdown
@@ -207,51 +199,10 @@ Explore detailed MultiRepoOps examples:
## Best Practices
-### Authentication and Security
-
-- Use GitHub Apps for automatic token revocation
-- Scope PATs minimally to required repositories
-- Rotate tokens regularly
-- Store tokens as GitHub secrets (never in code)
-
-### Workflow Design
-
-- Set appropriate `max` limits on safe outputs
-- Use meaningful title prefixes for tracking
-- Apply consistent labels across repositories
-- Include clear documentation in created items
-
-### Error Handling
-
-- Validate repository access before operations
-- Handle rate limits appropriately
-- Provide fallback for permission failures
-- Monitor workflow execution across repositories
-
-### Testing
-
-- Test with public repositories first
-- Pilot with small repository subset
-- Verify path mappings and configurations
-- Monitor costs and rate limits
-
-## Advanced Topics
-
-### Private Repository Access
-
-When working with private repositories:
-- Ensure PAT owner has repository access
-- Install GitHub Apps in target organizations
-- Configure repository lists explicitly
-- Test permissions before full rollout
-
-### Organization-Level Operations
-
-For organization-wide workflows:
-- Use organization-level secrets
-- Configure GitHub Apps at organization level
-- Plan phased rollouts
-- Provide clear communication
+- **Authentication**: Use GitHub Apps for automatic token revocation; scope PATs minimally to required repositories; store tokens as GitHub secrets
+- **Workflow design**: Set appropriate `max` limits; use meaningful title prefixes and consistent labels
+- **Error handling**: Handle rate limits and permission failures; monitor workflow execution across repositories
+- **Testing**: Start with public repositories, pilot a small subset, and verify configurations before full rollout
## Related Patterns
diff --git a/docs/src/content/docs/reference/auth-projects.mdx b/docs/src/content/docs/reference/auth-projects.mdx
new file mode 100644
index 00000000000..6ec7492fb41
--- /dev/null
+++ b/docs/src/content/docs/reference/auth-projects.mdx
@@ -0,0 +1,76 @@
+---
+title: Authentication (Projects)
+description: Reference for authenticating GitHub Projects read and write operations in gh-aw
+sidebar:
+ order: 655
+---
+
+GitHub Projects operations require additional authentication because the default `GITHUB_TOKEN` is repository-scoped and cannot access the Projects GraphQL API for read or write operations.
+
+## Why a separate token is needed
+
+The standard `GITHUB_TOKEN` provided to every GitHub Actions workflow has repository-level scope only. GitHub Projects (both user-owned and organization-owned) sit outside that scope, so any workflow step that reads project fields or writes updates must supply a token with explicit Projects permissions.
+
+This applies to:
+
+- [GitHub tools `projects` toolset](/gh-aw/reference/github-tools/#additional-authentication-for-github-tools) — reads project items and field values
+- [`update-project` safe output](/gh-aw/reference/safe-outputs/#project-board-updates-update-project) — adds items and updates fields
+- [`create-project` safe output](/gh-aw/reference/safe-outputs/#project-creation-create-project) — creates new project boards
+- [`create-project-status-update` safe output](/gh-aw/reference/safe-outputs/#project-status-updates-create-project-status-update) — posts status updates
+
+## Personal Access Tokens
+
+### User-owned projects
+
+Use a [classic PAT](https://github.com/settings/tokens/new) with the following scopes:
+
+- `project`
+- `repo` (required if the project contains items from private repositories)
+
+### Organization-owned projects
+
+Use a [fine-grained PAT](https://github.com/settings/personal-access-tokens/new?name=GH_AW_WRITE_PROJECT_TOKEN&description=GitHub+Agentic+Workflows+-+Projects+authentication&contents=read&issues=read&pull_requests=read) with these settings:
+
+- **Resource owner**: the organization that owns the project
+- **Repository access**: the repositories that will run the workflow
+- **Repository permissions**: `Contents: Read`, and optionally `Issues: Read` / `Pull requests: Read`
+- **Organization permissions**: `Projects: Read and write`
+
+## GitHub App tokens
+
+For organization-wide standardization, a GitHub App can be used instead of PATs. The app must have **Organization projects: Read and write** permission.
+
+See [Using a GitHub App for Authentication](/gh-aw/reference/auth/#using-a-github-app-for-authentication) for setup instructions.
+
+## Recommended secret layout
+
+Use separate read and write tokens to enforce least privilege:
+
+```bash wrap
+gh aw secrets set GH_AW_READ_PROJECT_TOKEN --value ""
+gh aw secrets set GH_AW_WRITE_PROJECT_TOKEN --value ""
+```
+
+Reference each token in the workflow where it is needed:
+
+```aw wrap
+tools:
+ github:
+ mode: remote
+ toolsets: [projects]
+ github-token: ${{ secrets.GH_AW_READ_PROJECT_TOKEN }}
+
+safe-outputs:
+ update-project:
+ project-url: https://github.com/orgs/my-org/projects/1
+ github-token: ${{ secrets.GH_AW_WRITE_PROJECT_TOKEN }}
+```
+
+The magic secret `GH_AW_GITHUB_MCP_SERVER_TOKEN` is recognized by GitHub Agentic Workflows and does not need to be explicitly referenced in your workflow — if it is present in the repository, it is used automatically for all GitHub tools toolsets, including `projects`.
+
+## Related documentation
+
+- [Authentication](/gh-aw/reference/auth/) — AI engine secrets and GitHub App setup
+- [GitHub Tools](/gh-aw/reference/github-tools/) — toolset configuration and additional authentication
+- [Safe Outputs](/gh-aw/reference/safe-outputs/) — write operations and token configuration
+- [ProjectOps pattern](/gh-aw/patterns/project-ops/) — end-to-end example with project boards
diff --git a/docs/src/content/docs/reference/dependencies.md b/docs/src/content/docs/reference/dependencies.md
index 710e52cb1fe..5a0136b73ba 100644
--- a/docs/src/content/docs/reference/dependencies.md
+++ b/docs/src/content/docs/reference/dependencies.md
@@ -34,6 +34,29 @@ dependencies:
isolated: true # clear repo primitives before unpack (default: false)
```
+### Cross-org private packages with GitHub App authentication
+
+Use `github-app:` when packages live in a different organization and require a GitHub App token for access. The token is minted before the APM pack step so APM can reach the private repository.
+
+```yaml wrap
+dependencies:
+ github-app:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ repositories:
+ - acme-skills # or use ["*"] for all repos in the app installation owner's org
+ packages:
+ - acme-platform-org/acme-skills/plugins/dev-tools
+ - acme-platform-org/acme-skills/skills/code-review
+```
+
+| Field | Required | Description |
+|-------|----------|-------------|
+| `github-app.app-id` | Yes | GitHub App ID (e.g., `${{ vars.APP_ID }}`) |
+| `github-app.private-key` | Yes | GitHub App private key (e.g., `${{ secrets.APP_PRIVATE_KEY }}`) |
+| `github-app.owner` | No | Installation owner (defaults to current repository owner) |
+| `github-app.repositories` | No | Repositories to grant access to. Use `["*"]` for all repos in the owner's installation |
+
## Package reference formats
Each entry is an APM package reference. Supported formats:
diff --git a/docs/src/content/docs/reference/faq.md b/docs/src/content/docs/reference/faq.md
index c599dbc754c..b6a1546a381 100644
--- a/docs/src/content/docs/reference/faq.md
+++ b/docs/src/content/docs/reference/faq.md
@@ -205,6 +205,8 @@ See [Network Permissions](/gh-aw/reference/network/) for complete configuration
Lockdown mode is **automatically enabled** for public repositories if [Additional Authentication for GitHub Tools](/gh-aw/reference/github-tools/#additional-authentication-for-github-tools) is configured. It is not in effect for private or internal repositories.
+In addition, for **public repositories** where the GitHub MCP server is not explicitly configured with `lockdown` or `min-integrity`, `min-integrity: approved` is automatically applied at runtime. This provides equivalent protection — restricting content to owners, members, and collaborators — even without additional authentication.
+
## Configuration & Setup
### What is a workflow lock file?
diff --git a/docs/src/content/docs/reference/frontmatter.md b/docs/src/content/docs/reference/frontmatter.md
index e51d938a77b..ec524b299d0 100644
--- a/docs/src/content/docs/reference/frontmatter.md
+++ b/docs/src/content/docs/reference/frontmatter.md
@@ -37,6 +37,8 @@ The `on:` section uses standard GitHub Actions syntax to define workflow trigger
- `skip-bots:` - Skip workflow execution for specific GitHub actors
- `skip-if-match:` - Skip execution when a search query has matches (supports `scope: none`; use top-level `on.github-token` / `on.github-app` for custom auth)
- `skip-if-no-match:` - Skip execution when a search query has no matches (supports `scope: none`; use top-level `on.github-token` / `on.github-app` for custom auth)
+- `steps:` - Inject custom deterministic steps into the pre-activation job (saves one workflow job vs. multi-job pattern)
+- `permissions:` - Grant additional GitHub token scopes to the pre-activation job (for use with `on.steps:` API calls)
- `github-token:` - Custom token for activation job reactions, status comments, and skip-if search queries
- `github-app:` - GitHub App for minting a short-lived token used by the activation job and all skip-if search steps
@@ -156,13 +158,24 @@ Each plugin repository must be specified in `org/repo` format. The compiler gene
Specifies [APM (Agent Package Manager)](https://microsoft.github.io/apm/) packages to install before workflow execution. APM manages AI agent primitives such as skills, prompts, instructions, agents, hooks, and plugins (including the Claude `plugin.json` format). When present, the compiler runs `apm pack` in the activation job and `apm unpack` in the agent job for faster, deterministic startup.
```yaml wrap
+# Simple array format (public or same-org packages)
dependencies:
- microsoft/apm-sample-package
- github/awesome-copilot/skills/review-and-refactor
- microsoft/apm-sample-package#v2.0 # version-pinned
```
-See **[APM Dependencies Reference](/gh-aw/reference/dependencies/)** for the full format specification, version pinning syntax, plugin support, reproducibility and governance details, and local debugging instructions.
+```yaml wrap
+# Object format with GitHub App auth for cross-org private packages
+dependencies:
+ github-app:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+ packages:
+ - acme-platform-org/acme-skills/plugins/dev-tools
+```
+
+See **[APM Dependencies Reference](/gh-aw/reference/dependencies/)** for the full format specification, version pinning syntax, GitHub App authentication, plugin support, reproducibility and governance details, and local debugging instructions.
### Runtimes (`runtimes:`)
diff --git a/docs/src/content/docs/reference/github-tools.md b/docs/src/content/docs/reference/github-tools.md
index cacd810147d..206af5b4a5d 100644
--- a/docs/src/content/docs/reference/github-tools.md
+++ b/docs/src/content/docs/reference/github-tools.md
@@ -63,6 +63,8 @@ Guard policy fields (`repos` and `min-integrity`) are experimental and may chang
Restrict which repositories and integrity levels the GitHub MCP server can access during agent execution. Guard policies apply fine-grained access control at the MCP gateway level.
+For **public repositories** without explicit guard policy configuration, `min-integrity: approved` is applied automatically at runtime, ensuring content is filtered to owners, members, and collaborators even without additional authentication. See [Automatic Minimum-Integrity Protection](/gh-aw/reference/lockdown-mode/#automatic-minimum-integrity-protection) for details.
+
```yaml wrap
tools:
github:
diff --git a/docs/src/content/docs/reference/glossary.md b/docs/src/content/docs/reference/glossary.md
index 63bd82b4636..efe1f5852ed 100644
--- a/docs/src/content/docs/reference/glossary.md
+++ b/docs/src/content/docs/reference/glossary.md
@@ -110,7 +110,9 @@ An optional field on safe output tool calls indicating the trustworthiness level
### Lockdown Mode
-A security feature of the GitHub MCP server that filters content in public repositories to only surface items (issues, pull requests, comments, discussions) from users with push access. Protects agentic workflows from processing potentially malicious or misleading content submitted by untrusted users. Enabled via `lockdown: true` in the `tools.github` section. See [Lockdown Mode](/gh-aw/reference/lockdown-mode/).
+A security feature of the GitHub MCP server that filters content in public repositories to only surface items (issues, pull requests, comments, discussions) from users with push access. Protects agentic workflows from processing potentially malicious or misleading content submitted by untrusted users.
+
+For **public repositories**, `min-integrity: approved` is automatically applied at runtime when no explicit `lockdown` or `min-integrity` guard policy is configured — providing the same filtering level as lockdown without requiring additional authentication. Explicit `lockdown: true` requires a custom `github-token` and is automatically enabled for public repositories when one is configured. Set `min-integrity: none` or `lockdown: false` to disable for workflows designed to process content from all users. See [Lockdown Mode](/gh-aw/reference/lockdown-mode/).
### Status Comment
@@ -204,10 +206,24 @@ Custom GitHub token or GitHub App used by the activation job to post reactions a
A time-based trigger format. Use short syntax like `daily` or `weekly on monday` (recommended with automatic time scattering) or standard cron expressions for fixed times. See also Fuzzy Scheduling and Time Scattering.
+### Ecosystem Identifiers
+
+Named shorthand references to predefined domain sets used in `network.allowed` and `safe-outputs.allowed-domains`. Instead of listing individual domain names, ecosystem identifiers expand to curated sets for a language runtime or service category. Common identifiers: `python` (PyPI/pip), `node` (npm), `go` (proxy.golang.org), `github` (GitHub domains), `dev-tools` (CI/CD services such as Codecov, Snyk, Shields.io), `local` (loopback addresses), and `default-safe-outputs` (a compound set combining `defaults` + `dev-tools` + `github` + `local`, recommended as a baseline for `safe-outputs.allowed-domains`). See [Network Permissions Reference](/gh-aw/reference/network/#ecosystem-identifiers).
+
### Engine
The AI system that powers the agentic workflow - essentially "which AI to use" to execute workflow instructions. GitHub Agentic Workflows supports multiple engines, with GitHub Copilot as the default.
+### Enterprise API Endpoint (`api-target`)
+
+An `engine` configuration field specifying a custom API endpoint hostname for GitHub Enterprise Cloud (GHEC) or GitHub Enterprise Server (GHES) deployments. When set, the compiler automatically adds both the API domain and the base hostname to the AWF firewall `--allow-domains` list and the `GH_AW_ALLOWED_DOMAINS` environment variable, eliminating the need for manual network configuration after each recompile. The value must be a hostname only — no protocol or path (e.g., `api.acme.ghe.com`). See [Engines Reference](/gh-aw/reference/engines/#enterprise-api-endpoint-api-target).
+
+```aw wrap
+engine:
+ id: copilot
+ api-target: api.acme.ghe.com
+```
+
### Inline Engine Definition
An engine configuration format that specifies a runtime adapter and optional provider settings directly in workflow frontmatter, without requiring a named catalog entry. Uses a `runtime` object (with `id` and optional `version`) to identify the adapter and an optional `provider` object for model selection, authentication, and request shaping. Useful for connecting to self-hosted or third-party AI backends.
@@ -388,6 +404,19 @@ A system-injected environment variable identifying the active execution phase. S
A system-injected environment variable containing the gh-aw compiler version that generated the workflow (e.g. `"0.40.1"`). Useful for writing conditional logic that depends on a minimum feature version. Cannot be overridden by user-defined `env:` blocks. See [Environment Variables Reference](/gh-aw/reference/environment-variables/).
+### `GH_AW_ALLOWED_DOMAINS`
+
+A system-injected environment variable containing the comma-separated list of domains allowed by the workflow's network configuration. Used by safe output jobs for URL sanitization — URLs from unlisted domains are redacted in AI-generated content before it is applied. Automatically populated from `network.allowed` domains and, when `engine.api-target` is set, includes the GHES/GHEC API hostname and base domain. Cannot be overridden by user-defined `env:` blocks. See [Environment Variables Reference](/gh-aw/reference/environment-variables/).
+
+### Label Command Trigger (`label_command`)
+
+A trigger that activates a workflow when a specific label is added to an issue, pull request, or discussion. Unlike standard label filtering, the label command trigger automatically removes the applied label on activation so it can be reapplied to re-trigger the workflow. Configured via `label_command:` in the `on:` section; exposes `needs.activation.outputs.label_command` with the matched label name for downstream jobs. Can be combined with `slash_command:` to support both label-based and comment-based triggering. See [LabelOps patterns](/gh-aw/patterns/label-ops/).
+
+```yaml wrap
+on:
+ label_command: deploy
+```
+
### Repo Memory
Persistent file storage via Git branches with unlimited retention. Unlike cache-memory (7-day retention), repo-memory stores files permanently in dedicated Git branches with automatic branch cloning, file access, commits, pushes, and merge conflict resolution. Setting `wiki: true` switches the backing to the GitHub Wiki's git endpoint (`{repo}.wiki.git`), and the agent receives guidance to follow GitHub Wiki Markdown conventions (e.g. `[[Page Name]]` links). See [Repo Memory](/gh-aw/reference/repo-memory/).
diff --git a/docs/src/content/docs/reference/lockdown-mode.md b/docs/src/content/docs/reference/lockdown-mode.md
index b6f1a66b494..9f3cb44dd9f 100644
--- a/docs/src/content/docs/reference/lockdown-mode.md
+++ b/docs/src/content/docs/reference/lockdown-mode.md
@@ -10,6 +10,29 @@ sidebar:
> [!IMPORTANT]
> Workflows running on public repositories must be compiled with strict mode enabled. If `strict: false` is set in the frontmatter, the workflow will fail at runtime on public repositories. See [Strict Mode](/gh-aw/reference/frontmatter/#strict-mode-strict) for details.
+## Automatic Minimum-Integrity Protection
+
+For **public repositories** where the GitHub MCP server is configured **without** explicit `lockdown` or `min-integrity` guard policy settings, `min-integrity: approved` is automatically applied at runtime. This ensures the guardrail is always in place — even when additional authentication has not been configured.
+
+`min-integrity: approved` restricts content to objects authored by owners, members, and collaborators (users with push access), providing the same level of content filtering as enabling lockdown mode explicitly.
+
+- **Public repositories**: `min-integrity: approved` is applied automatically (same filtering level as explicit lockdown mode).
+- **Private/internal repositories**: No guard policy is applied automatically (`min-integrity: none`).
+
+The automatic guard policy does **not** apply when:
+- An explicit `lockdown` or `min-integrity` value is set in the workflow frontmatter.
+- A GitHub App token is configured (`tools.github.app`).
+
+To override or disable the automatic guard policy, set an explicit value:
+
+```yaml wrap
+tools:
+ github:
+ min-integrity: none # Disable automatic guard for public repo workflows that process all users
+```
+
+## Lockdown Mode (Content Filter)
+
To enable lockdown mode for your workflow:
1. **Set `lockdown: true` in your workflow frontmatter**
diff --git a/docs/src/content/docs/reference/mcp-scripts.md b/docs/src/content/docs/reference/mcp-scripts.md
index 93359c3c7cf..aea9bddbd05 100644
--- a/docs/src/content/docs/reference/mcp-scripts.md
+++ b/docs/src/content/docs/reference/mcp-scripts.md
@@ -56,46 +56,11 @@ mcp-scripts:
timeout: 120 # Optional: timeout in seconds (default: 60)
```
-### Required Fields
-
-- **`description:`** - Human-readable description of what the tool does. This is shown to the agent for tool selection.
-
-### Optional Fields
-
-- **`timeout:`** - Maximum execution time in seconds (default: 60). The tool will be terminated if it exceeds this duration. Applies to shell (`run:`) and Python (`py:`) tools.
-
-### Implementation Options
-
-Choose one implementation method:
-
-- **`script:`** - JavaScript (CommonJS) code
-- **`run:`** - Shell script
-- **`py:`** - Python script (Python 3.1x)
-- **`go:`** - Go (Golang) code
-
-You can only use one of `script:`, `run:`, `py:`, or `go:` per tool.
+Each tool requires `description:` and exactly one of `script:`, `run:`, `py:`, or `go:`.
## JavaScript Tools (`script:`)
-JavaScript tools are automatically wrapped in an async function with destructured inputs:
-
-```yaml wrap
-mcp-scripts:
- calculate-sum:
- description: "Add two numbers"
- inputs:
- a:
- type: number
- required: true
- b:
- type: number
- required: true
- script: |
- const result = a + b;
- return { sum: result };
-```
-
-Your script is wrapped as `async function execute(inputs)` with inputs destructured. Access secrets via `process.env`:
+JavaScript tools wrap your `script:` in `async function execute(inputs)` with inputs destructured. Access secrets via `process.env`:
```yaml wrap
mcp-scripts:
@@ -195,35 +160,7 @@ mcp-scripts:
Your Go code receives `inputs map[string]any` from stdin and should output JSON to stdout. The code is wrapped in a `package main` with a `main()` function that handles input parsing.
-**Available by default:**
-- `encoding/json` - JSON encoding/decoding
-- `fmt` - Formatted I/O
-- `io` - I/O primitives
-- `os` - Operating system functionality
-
-Access environment variables (including secrets) using `os.Getenv()`:
-
-```yaml wrap
-mcp-scripts:
- api-call:
- description: "Call an API with Go"
- inputs:
- endpoint:
- type: string
- required: true
- go: |
- apiKey := os.Getenv("API_KEY")
- endpoint := inputs["endpoint"].(string)
-
- // Make your API call here
- result := map[string]any{
- "endpoint": endpoint,
- "authenticated": apiKey != "",
- }
- json.NewEncoder(os.Stdout).Encode(result)
- env:
- API_KEY: "${{ secrets.API_KEY }}"
-```
+Access secrets via `os.Getenv("VAR_NAME")` (see [Environment Variables](#environment-variables-env) for the `env:` field).
## Input Parameters
@@ -248,21 +185,6 @@ mcp-scripts:
description: "Limited to specific values"
```
-### Supported Types
-
-- `string` - Text values
-- `number` - Numeric values
-- `boolean` - True/false values
-- `array` - List of values
-- `object` - Structured data
-
-### Validation Options
-
-- `required: true` - Parameter must be provided
-- `default: value` - Default if not provided
-- `enum: [...]` - Restrict to specific values
-- `description: "..."` - Help text for the agent
-
## Timeout Configuration
Set execution timeout with `timeout:` field (default: 60 seconds):
@@ -346,9 +268,6 @@ Analyze provided text using the `analyze-text` tool and create a discussion with
MCP Scripts tools run on the GitHub Actions **runner host** — outside the agent container — so they can access the runner's file system and environment but are isolated from the AI's own execution environment. Tools also provide secret isolation (only specified env vars are forwarded), process isolation (separate execution), and output sanitization (large outputs saved to files). Only predefined tools are available to agents.
-> [!CAUTION]
-> Because MCP Scripts execute outside the sandbox, **only implement READ-ONLY tools**. Write operations (file writes, API mutations, repository changes) must be handled through [Safe Output Jobs](/gh-aw/reference/safe-outputs/) or [Custom Safe Output Jobs](/gh-aw/reference/custom-safe-outputs/), which provide proper audit trails and approval gates.
-
## Comparison with Other Options
| Feature | MCP Scripts | Custom MCP Servers | Bash Tool |
diff --git a/docs/src/content/docs/reference/network.md b/docs/src/content/docs/reference/network.md
index 5e8fc1761c8..63df56a3521 100644
--- a/docs/src/content/docs/reference/network.md
+++ b/docs/src/content/docs/reference/network.md
@@ -94,7 +94,10 @@ Mix ecosystem identifiers with specific domains for fine-grained control:
| Identifier | Includes |
|------------|----------|
| `defaults` | Basic infrastructure (certificates, JSON schema, Ubuntu, package mirrors) |
-| `github` | GitHub domains |
+| `github` | GitHub domains (`github.com`, `docs.github.com`, `github.blog`, `*.githubusercontent.com`, and related) |
+| `local` | Loopback addresses (`localhost`, `127.0.0.1`, `::1`) |
+| `dev-tools` | Popular CI/CD and developer tool services (Codecov, Shields.io, Snyk, Renovate, CircleCI, etc.) |
+| `default-safe-outputs` | Compound: `defaults` + `dev-tools` + `github` + `local` — recommended baseline for `safe-outputs.allowed-domains` |
| `containers` | Docker Hub, GitHub Container Registry, Quay |
| `linux-distros` | Debian, Alpine, and other Linux package repositories |
| `dotnet`, `dart`, `go`, `haskell`, `java`, `julia`, `node`, `perl`, `php`, `python`, `ruby`, `rust`, `swift` | Language-specific package managers and registries |
diff --git a/docs/src/content/docs/reference/safe-outputs.md b/docs/src/content/docs/reference/safe-outputs.md
index 958ac2bac33..a3ef9091a2f 100644
--- a/docs/src/content/docs/reference/safe-outputs.md
+++ b/docs/src/content/docs/reference/safe-outputs.md
@@ -1150,7 +1150,9 @@ safe-outputs:
#### Worker Inputs
-Worker inputs are forwarded as a single JSON-encoded `payload` string, avoiding GitHub's per-`workflow_call` limit. Define the input in the worker:
+Worker inputs are forwarded via two complementary mechanisms.
+
+**Canonical transport — `payload`:** The runtime serializes all agent-provided arguments into a single JSON string and writes it to the `call_workflow_payload` step output. The compiler always includes this in the generated `with:` block:
```yaml title="spring-boot-bugfix.md (worker)"
on:
@@ -1161,12 +1163,15 @@ on:
required: false
```
-The compiler also reads typed inputs declared on the worker and exposes them as parameters on the generated MCP tool, so the agent can provide structured values:
+**Typed inputs — compiler-derived forwarding:** When a worker declares additional `workflow_call.inputs` beyond `payload`, the compiler reads those declarations and emits one extra `with:` entry per input in the fan-out job using `fromJSON(needs.safe_outputs.outputs.call_workflow_payload).`. This means worker steps can reference `inputs.` directly, without manually parsing the JSON envelope:
```yaml title="deploy.md (worker)"
on:
workflow_call:
inputs:
+ payload:
+ type: string
+ required: false
environment:
description: Target environment
type: choice
@@ -1181,7 +1186,7 @@ Supported input types: `string`, `number`, `boolean`, `choice` (rendered as an e
#### Compiled Output
-For each worker the compiler emits a conditional `uses:` job in the lock file:
+For each worker the compiler emits a conditional `uses:` job in the lock file. The `with:` block always includes the canonical `payload` entry; for each declared worker input other than `payload`, the compiler also emits a `fromJSON`-derived entry so worker steps can use `${{ inputs. }}` directly:
```yaml title="gateway.lock.yml (simplified)"
safe_outputs:
@@ -1196,8 +1201,12 @@ call-spring-boot-bugfix:
secrets: inherit
with:
payload: ${{ needs.safe_outputs.outputs.call_workflow_payload }}
+ environment: ${{ fromJSON(needs.safe_outputs.outputs.call_workflow_payload).environment }}
+ dry_run: ${{ fromJSON(needs.safe_outputs.outputs.call_workflow_payload).dry_run }}
```
+`payload` remains the canonical transport; the additional entries are compiler-derived projections of the same data so that worker steps can reference `inputs.` without parsing JSON.
+
#### Validation Rules
At compile time, the compiler validates:
@@ -1416,7 +1425,21 @@ safe-outputs:
allowed-github-references: [] # Escape all GitHub references
```
-**Domain Filtering** (`allowed-domains`): Controls which domains are allowed in URLs. URLs from other domains are replaced with `(redacted)`.
+**Domain Filtering** (`allowed-domains`): Controls which domains are allowed in URLs. URLs from other domains are replaced with `(redacted)`. Accepts specific domain strings or [ecosystem identifiers](/gh-aw/reference/network/#ecosystem-identifiers):
+
+```yaml wrap
+safe-outputs:
+ # Allow specific domains
+ allowed-domains: [api.example.com, "*.storage.example.com"]
+
+ # Use ecosystem identifiers
+ allowed-domains: [default-safe-outputs] # defaults + dev-tools + github + local
+
+ # Mix identifiers and custom domains
+ allowed-domains: [default-safe-outputs, api.example.com]
+```
+
+The `default-safe-outputs` compound ecosystem is the recommended baseline — it covers infrastructure certificates (`defaults`), GitHub domains (`github`), popular developer tooling (`dev-tools`), and loopback addresses (`local`).
**Reference Escaping** (`allowed-github-references`): Controls which GitHub repository references (`#123`, `owner/repo#456`) are allowed in workflow output. When configured, references to unlisted repositories are escaped with backticks to prevent GitHub from creating timeline items. This is particularly useful for [SideRepoOps](/gh-aw/patterns/side-repo-ops/) workflows to prevent automation from cluttering your main repository's timeline.
diff --git a/docs/src/content/docs/reference/triggers.md b/docs/src/content/docs/reference/triggers.md
index b1fead53ad6..da20e621d21 100644
--- a/docs/src/content/docs/reference/triggers.md
+++ b/docs/src/content/docs/reference/triggers.md
@@ -282,9 +282,32 @@ See the [Security Architecture](/gh-aw/introduction/architecture/) for details.
The `slash_command:` trigger creates workflows that respond to `/command-name` mentions in issues, pull requests, and comments. See [Command Triggers](/gh-aw/reference/command-triggers/) for complete documentation including event filtering, context text, reactions, and examples.
+### Label Command Trigger (`label_command:`)
+
+The `label_command:` trigger activates a workflow when a specific label is applied to an issue, pull request, or discussion, and **automatically removes that label** so it can be re-applied to re-trigger. This treats a label as a one-shot command rather than a persistent state marker.
+
+```yaml wrap
+# Fires on issues, pull_request, and discussion by default
+on:
+ label_command: deploy
+
+# Restrict to specific event types
+on:
+ label_command:
+ name: deploy
+ events: [pull_request]
+
+# Shorthand string form
+on: "label-command deploy"
+```
+
+The compiler generates `issues`, `pull_request`, and/or `discussion` events with `types: [labeled]`, adds a `workflow_dispatch` trigger with `item_number` for manual testing, and injects a label removal step in the activation job. The matched label name is exposed as `needs.activation.outputs.label_command`.
+
+`label_command` can be combined with `slash_command:` — the workflow activates when either condition is met. See [LabelOps](/gh-aw/patterns/label-ops/) for patterns and examples.
+
### Label Filtering (`names:`)
-Filter issue and pull request triggers by label names using the `names:` field:
+Filter issue and pull request triggers by label names using the `names:` field. Unlike `label_command`, the label stays on the item after the workflow runs.
```yaml wrap
on:
@@ -473,11 +496,91 @@ on:
owner: myorg
```
+### Pre-Activation Steps (`on.steps:`)
+
+Inject custom deterministic steps directly into the pre-activation job. Steps run after all built-in checks (membership, stop-time, skip-if, etc.) and **before** agent execution. This saves one workflow job compared to the multi-job pattern and keeps filtering logic co-located with the trigger configuration.
+
+```yaml wrap
+on:
+ issues:
+ types: [opened]
+ steps:
+ - name: Check issue label
+ id: label_check
+ env:
+ LABELS: ${{ toJSON(github.event.issue.labels.*.name) }}
+ run: echo "$LABELS" | grep -q '"bug"'
+ # exits 0 (outcome: success) if the label is found, 1 (outcome: failure) if not
+
+if: needs.pre_activation.outputs.label_check_result == 'success'
+```
+
+Each step with an `id` automatically gets an output `_result` wired to `${{ steps..outcome }}` (values: `success`, `failure`, `cancelled`, `skipped`). This lets you gate the workflow on whether the step **succeeded or failed** via its exit code.
+
+To pass an explicit value rather than relying on exit codes, set a step output and re-expose it via `jobs.pre-activation.outputs`:
+
+```yaml wrap
+on:
+ issues:
+ types: [opened]
+ steps:
+ - name: Check issue label
+ id: label_check
+ env:
+ LABELS: ${{ toJSON(github.event.issue.labels.*.name) }}
+ run: |
+ if echo "$LABELS" | grep -q '"bug"'; then
+ echo "has_bug_label=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "has_bug_label=false" >> "$GITHUB_OUTPUT"
+ fi
+
+jobs:
+ pre-activation:
+ outputs:
+ has_bug_label: ${{ steps.label_check.outputs.has_bug_label }}
+
+if: needs.pre_activation.outputs.has_bug_label == 'true'
+```
+
+Explicit outputs defined in `jobs.pre-activation.outputs` take precedence over auto-wired `_result` outputs on key collision.
+
+### Pre-Activation Permissions (`on.permissions:`)
+
+Grant additional GitHub token permission scopes to the pre-activation job. Use when `on.steps:` make GitHub API calls that require permissions beyond the defaults.
+
+```yaml wrap
+on:
+ schedule: every 30m
+ permissions:
+ issues: read
+ pull-requests: read
+ steps:
+ - name: Search for candidate issues
+ id: search
+ uses: actions/github-script@v8
+ with:
+ script: |
+ const issues = await github.rest.issues.listForRepo(context.repo);
+ core.setOutput('has_issues', issues.data.length > 0 ? 'true' : 'false');
+
+jobs:
+ pre-activation:
+ outputs:
+ has_issues: ${{ steps.search.outputs.has_issues }}
+
+if: needs.pre_activation.outputs.has_issues == 'true'
+```
+
+Supported permission scopes: `actions`, `checks`, `contents`, `deployments`, `discussions`, `issues`, `packages`, `pages`, `pull-requests`, `repository-projects`, `security-events`, `statuses`.
+
+`on.permissions` is merged on top of any permissions already required by the pre-activation job (e.g., `contents: read` for dev-mode checkout, `actions: read` for rate limiting).
+
## Trigger Shorthands
Instead of writing full YAML trigger configurations, you can use natural-language shorthand strings with `on:`. The compiler expands these into standard GitHub Actions trigger syntax and automatically includes `workflow_dispatch` so the workflow can also be run manually.
-For label-based shorthands (`on: issue labeled bug`, `on: pull_request labeled needs-review`), see [Label Filtering](#label-filtering-names) above.
+For label-based shorthands (`on: issue labeled bug`, `on: pull_request labeled needs-review`), see [Label Filtering](#label-filtering-names) above. For the label-command pattern, see [Label Command Trigger](#label-command-trigger-label_command) above.
### Push and Pull Request
diff --git a/docs/src/content/docs/setup/cli.md b/docs/src/content/docs/setup/cli.md
index 5b420f69dc7..c814450ee88 100644
--- a/docs/src/content/docs/setup/cli.md
+++ b/docs/src/content/docs/setup/cli.md
@@ -511,7 +511,7 @@ gh aw mcp-server --validate-actor # Enable actor validation
**Options:** `--port` (HTTP server port), `--cmd` (custom subprocess command), `--validate-actor` (enforce actor validation for logs and audit tools)
-**Available Tools:** status, compile, logs, audit, mcp-inspect, add, update
+**Available Tools:** status, compile, logs, audit, mcp-inspect, add, update, fix
When `--validate-actor` is enabled, logs and audit tools require write+ repository access via GitHub API (permissions cached for 1 hour). See [MCP Server Guide](/gh-aw/reference/gh-aw-as-mcp-server/).
diff --git a/go.mod b/go.mod
index fc44cb2ac47..ec485d2ff0a 100644
--- a/go.mod
+++ b/go.mod
@@ -13,7 +13,7 @@ require (
github.com/fsnotify/fsnotify v1.9.0
github.com/goccy/go-yaml v1.19.2
github.com/google/jsonschema-go v0.4.2
- github.com/modelcontextprotocol/go-sdk v1.4.0
+ github.com/modelcontextprotocol/go-sdk v1.4.1
github.com/rhysd/actionlint v1.7.11
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
github.com/securego/gosec/v2 v2.24.7
@@ -82,7 +82,7 @@ require (
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/sahilm/fuzzy v0.1.1 // indirect
github.com/segmentio/asm v1.1.3 // indirect
- github.com/segmentio/encoding v0.5.3 // indirect
+ github.com/segmentio/encoding v0.5.4 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/thlib/go-timezone-local v0.0.7 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
diff --git a/go.sum b/go.sum
index 7d2f91f1e74..4fbec5e26ce 100644
--- a/go.sum
+++ b/go.sum
@@ -148,8 +148,8 @@ github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebG
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
-github.com/modelcontextprotocol/go-sdk v1.4.0 h1:u0kr8lbJc1oBcawK7Df+/ajNMpIDFE41OEPxdeTLOn8=
-github.com/modelcontextprotocol/go-sdk v1.4.0/go.mod h1:Nxc2n+n/GdCebUaqCOhTetptS17SXXNu9IfNTaLDi1E=
+github.com/modelcontextprotocol/go-sdk v1.4.1 h1:M4x9GyIPj+HoIlHNGpK2hq5o3BFhC+78PkEaldQRphc=
+github.com/modelcontextprotocol/go-sdk v1.4.1/go.mod h1:Bo/mS87hPQqHSRkMv4dQq1XCu6zv4INdXnFZabkNU6s=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
@@ -183,8 +183,8 @@ github.com/securego/gosec/v2 v2.24.7 h1:3k5yJnrhT1TTdsG0ZsnenlfCcT+7Y/+zeCPHbL7Q
github.com/securego/gosec/v2 v2.24.7/go.mod h1:AdDJbjcG/XxFgVv7pW19vMNYlFM6+Q6Qy3t6lWAUcEY=
github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc=
github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
-github.com/segmentio/encoding v0.5.3 h1:OjMgICtcSFuNvQCdwqMCv9Tg7lEOXGwm1J5RPQccx6w=
-github.com/segmentio/encoding v0.5.3/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0=
+github.com/segmentio/encoding v0.5.4 h1:OW1VRern8Nw6ITAtwSZ7Idrl3MXCFwXHPgqESYfvNt0=
+github.com/segmentio/encoding v0.5.4/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
diff --git a/pkg/cli/actions_build_command.go b/pkg/cli/actions_build_command.go
index f535858d4f2..7bed495b30e 100644
--- a/pkg/cli/actions_build_command.go
+++ b/pkg/cli/actions_build_command.go
@@ -12,6 +12,7 @@ import (
"github.com/github/gh-aw/pkg/console"
"github.com/github/gh-aw/pkg/logger"
+ "github.com/github/gh-aw/pkg/sliceutil"
"github.com/github/gh-aw/pkg/workflow"
)
@@ -137,14 +138,11 @@ func getActionDirectories(actionsDir string) ([]string, error) {
return nil, fmt.Errorf("failed to read actions directory: %w", err)
}
- var dirs []string
- for _, entry := range entries {
- if entry.IsDir() {
- dirs = append(dirs, entry.Name())
- }
- }
+ dirEntries := sliceutil.Filter(entries, func(e os.DirEntry) bool { return e.IsDir() })
+ dirs := sliceutil.Map(dirEntries, func(e os.DirEntry) string { return e.Name() })
sort.Strings(dirs)
+ actionsBuildLog.Printf("Found %d action directories in %s", len(dirs), actionsDir)
return dirs, nil
}
@@ -162,6 +160,7 @@ func getActionDirectories(actionsDir string) ([]string, error) {
//
// This follows the principle that domain-specific validation belongs in domain files.
func validateActionYml(actionPath string) error {
+ actionsBuildLog.Printf("Validating action.yml: path=%s", actionPath)
ymlPath := filepath.Join(actionPath, "action.yml")
if _, err := os.Stat(ymlPath); os.IsNotExist(err) {
@@ -299,6 +298,7 @@ func isCompositeAction(actionPath string) (bool, error) {
// These files are manually edited and committed to git. They are NOT synced to pkg/workflow/
// At runtime, setup.sh copies these files to /tmp/gh-aw/actions for workflow execution.
func buildSetupAction(actionsDir, actionName string) error {
+ actionsBuildLog.Printf("Building setup action: actionsDir=%s, actionName=%s", actionsDir, actionName)
actionPath := filepath.Join(actionsDir, actionName)
jsDir := filepath.Join(actionPath, "js")
shDir := filepath.Join(actionPath, "sh")
diff --git a/pkg/cli/actions_build_command_test.go b/pkg/cli/actions_build_command_test.go
index 04469612d10..2860cfa5371 100644
--- a/pkg/cli/actions_build_command_test.go
+++ b/pkg/cli/actions_build_command_test.go
@@ -128,6 +128,19 @@ func TestGetActionDirectories(t *testing.T) {
}
}
+func TestGetActionDirectories_SortedOutput(t *testing.T) {
+ tmpDir := t.TempDir()
+ actionsDir := filepath.Join(tmpDir, "actions")
+ // Create directories in reverse-alphabetical order to verify sorting
+ for _, name := range []string{"zebra", "alpha", "middle"} {
+ require.NoError(t, os.MkdirAll(filepath.Join(actionsDir, name), 0755), "failed to create dir")
+ }
+
+ dirs, err := getActionDirectories(actionsDir)
+ require.NoError(t, err, "should not error with valid actions directory")
+ assert.Equal(t, []string{"alpha", "middle", "zebra"}, dirs, "directories should be sorted alphabetically")
+}
+
func TestValidateActionYml(t *testing.T) {
tests := []struct {
name string
diff --git a/pkg/cli/add_command.go b/pkg/cli/add_command.go
index 50fbc167b87..1c7305c933f 100644
--- a/pkg/cli/add_command.go
+++ b/pkg/cli/add_command.go
@@ -1,6 +1,7 @@
package cli
import (
+ "context"
"errors"
"fmt"
"os"
@@ -370,7 +371,7 @@ func addWorkflowWithTracking(resolved *ResolvedWorkflow, tracker *FileTracker, o
}
// Fetch and save workflows referenced in safe-outputs.dispatch-workflow so they are
// available locally. Workflow names using GitHub Actions expression syntax are skipped.
- if err := fetchAndSaveRemoteDispatchWorkflows(string(sourceContent), workflowSpec, githubWorkflowsDir, opts.Verbose, opts.Force, tracker); err != nil {
+ if err := fetchAndSaveRemoteDispatchWorkflows(context.Background(), string(sourceContent), workflowSpec, githubWorkflowsDir, opts.Verbose, opts.Force, tracker); err != nil {
return err
}
// Fetch files listed in the 'resources:' frontmatter field (additional workflow or
@@ -532,11 +533,11 @@ func addWorkflowWithTracking(resolved *ResolvedWorkflow, tracker *FileTracker, o
// Compile the workflow
if tracker != nil {
if err := compileWorkflowWithTracking(destFile, opts.Verbose, opts.Quiet, opts.EngineOverride, tracker); err != nil {
- fmt.Fprintln(os.Stderr, console.FormatErrorMessage(err.Error()))
+ fmt.Fprintln(os.Stderr, console.FormatErrorChain(err))
}
} else {
if err := compileWorkflow(destFile, opts.Verbose, opts.Quiet, opts.EngineOverride); err != nil {
- fmt.Fprintln(os.Stderr, console.FormatErrorMessage(err.Error()))
+ fmt.Fprintln(os.Stderr, console.FormatErrorChain(err))
}
}
diff --git a/pkg/cli/add_integration_test.go b/pkg/cli/add_integration_test.go
index 53b28a7e290..915e433b38e 100644
--- a/pkg/cli/add_integration_test.go
+++ b/pkg/cli/add_integration_test.go
@@ -978,7 +978,11 @@ func TestAddWorkflowWithDispatchWorkflowFromSharedImport(t *testing.T) {
// frontmatter. haiku-printer lives as haiku-printer.yml (a plain GitHub Actions
// workflow). The fetcher falls back to .yml when .md is 404, so both the main
// workflow and the dispatch-workflow dependency are written to disk.
- workflowSpec := "github/gh-aw/.github/workflows/smoke-copilot.md@main"
+ //
+ // Note: pinned to a specific commit SHA from the branch that renamed
+ // allowed-url-domains → allowed-domains (schema change). Update to @main once
+ // that change has been merged.
+ workflowSpec := "github/gh-aw/.github/workflows/smoke-copilot.md@c93eec8"
cmd := exec.Command(setup.binaryPath, "add", workflowSpec, "--verbose")
cmd.Dir = setup.tempDir
diff --git a/pkg/cli/add_interactive_engine.go b/pkg/cli/add_interactive_engine.go
index 9deb4109d49..fcd2e82ea33 100644
--- a/pkg/cli/add_interactive_engine.go
+++ b/pkg/cli/add_interactive_engine.go
@@ -73,7 +73,7 @@ func (c *AddInteractiveConfig) selectAIEngineAndKey() error {
// If engine is already overridden, skip selection
if c.EngineOverride != "" {
fmt.Fprintf(os.Stderr, "Using coding agent: %s\n", c.EngineOverride)
- return c.collectAPIKey(c.EngineOverride)
+ return c.configureEngineAPISecret(c.EngineOverride)
}
// Inform user if workflow specifies an engine
@@ -132,11 +132,11 @@ func (c *AddInteractiveConfig) selectAIEngineAndKey() error {
c.EngineOverride = selectedEngine
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("Selected engine: "+selectedEngine))
- return c.collectAPIKey(selectedEngine)
+ return c.configureEngineAPISecret(selectedEngine)
}
-// collectAPIKey collects the API key for the selected engine using the unified engine secrets functions
-func (c *AddInteractiveConfig) collectAPIKey(engine string) error {
+// configureEngineAPISecret collects the API key for the selected engine using the unified engine secrets functions
+func (c *AddInteractiveConfig) configureEngineAPISecret(engine string) error {
addInteractiveLog.Printf("Collecting API key for engine: %s", engine)
// If --skip-secret flag is set, skip secrets configuration entirely.
@@ -179,7 +179,7 @@ func (c *AddInteractiveConfig) collectAPIKey(engine string) error {
}
// Update existingSecrets to reflect that the secret was uploaded
- // This prevents duplicate secret uploads in applyChanges later
+ // This prevents duplicate secret uploads in createWorkflowPRAndConfigureSecret later
opt := constants.GetEngineOption(engine)
if opt != nil {
c.existingSecrets[opt.SecretName] = true
diff --git a/pkg/cli/add_interactive_git.go b/pkg/cli/add_interactive_git.go
index 11bc5b4bba2..9cb366cb7c2 100644
--- a/pkg/cli/add_interactive_git.go
+++ b/pkg/cli/add_interactive_git.go
@@ -14,8 +14,8 @@ import (
"github.com/github/gh-aw/pkg/workflow"
)
-// applyChanges creates the PR, merges it, and adds the secret
-func (c *AddInteractiveConfig) applyChanges(ctx context.Context, workflowFiles, initFiles []string, secretName, secretValue string) error {
+// createWorkflowPRAndConfigureSecret creates the PR, merges it, and adds the secret
+func (c *AddInteractiveConfig) createWorkflowPRAndConfigureSecret(ctx context.Context, workflowFiles, initFiles []string, secretName, secretValue string) error {
addInteractiveLog.Print("Applying changes")
fmt.Fprintln(os.Stderr, "")
@@ -56,6 +56,7 @@ func (c *AddInteractiveConfig) applyChanges(ctx context.Context, workflowFiles,
type mergeAction string
const (
mergeActionAttempt mergeAction = "attempt"
+ mergeActionEditTitle mergeAction = "editTitle"
mergeActionReview mergeAction = "review"
mergeActionConfirmed mergeAction = "confirmed"
mergeActionExit mergeAction = "exit"
@@ -69,8 +70,10 @@ func (c *AddInteractiveConfig) applyChanges(ctx context.Context, workflowFiles,
// Build option list based on current state
var options []huh.Option[mergeAction]
- if !mergeFailed {
- options = append(options, huh.NewOption("Attempt to merge", mergeActionAttempt))
+ options = append(options, huh.NewOption("Attempt to merge", mergeActionAttempt))
+
+ if mergeFailed {
+ options = append(options, huh.NewOption("Edit PR title and retry", mergeActionEditTitle))
}
if userReviewing {
@@ -107,7 +110,9 @@ func (c *AddInteractiveConfig) applyChanges(ctx context.Context, workflowFiles,
mergeDone = true
} else {
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to merge PR: %v", mergeErr)))
- fmt.Fprintln(os.Stderr, "Please merge the PR manually: "+result.PRURL)
+ if mergeFailed {
+ fmt.Fprintln(os.Stderr, "Please merge the PR manually: "+result.PRURL)
+ }
mergeFailed = true
}
} else {
@@ -115,6 +120,29 @@ func (c *AddInteractiveConfig) applyChanges(ctx context.Context, workflowFiles,
mergeDone = true
}
+ case mergeActionEditTitle:
+ var newTitle string
+ titleForm := huh.NewForm(
+ huh.NewGroup(
+ huh.NewInput().
+ Title("Enter new PR title").
+ Description("Add a prefix if required, for example: feat: or fix:").
+ Value(&newTitle),
+ ),
+ ).WithAccessible(console.IsAccessibleMode())
+ if titleErr := titleForm.Run(); titleErr != nil {
+ return fmt.Errorf("failed to get user input: %w", titleErr)
+ }
+ newTitle = strings.TrimSpace(newTitle)
+ if newTitle == "" {
+ fmt.Fprintln(os.Stderr, console.FormatWarningMessage("PR title cannot be empty, keeping current title"))
+ } else if editErr := editPRTitle(result.PRNumber, newTitle, c.RepoOverride); editErr != nil {
+ fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to update PR title: %v", editErr)))
+ } else {
+ fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("PR title updated to: "+newTitle))
+ mergeFailed = false
+ }
+
case mergeActionReview:
userReviewing = true
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Please review and merge the pull request: "+result.PRURL))
@@ -254,3 +282,16 @@ func (c *AddInteractiveConfig) mergePullRequest(prNumber int) error {
}
return nil
}
+
+// editPRTitle updates the title of the specified PR via the gh CLI.
+func editPRTitle(prNumber int, newTitle, repoOverride string) error {
+ args := []string{"pr", "edit", strconv.Itoa(prNumber), "--title", newTitle}
+ if repoOverride != "" {
+ args = append(args, "--repo", repoOverride)
+ }
+ output, err := workflow.RunGHCombined("Updating PR title...", args...)
+ if err != nil {
+ return fmt.Errorf("failed to update PR title: %w (output: %s)", err, string(output))
+ }
+ return nil
+}
diff --git a/pkg/cli/add_interactive_orchestrator.go b/pkg/cli/add_interactive_orchestrator.go
index 73090e372aa..a38b6f631f8 100644
--- a/pkg/cli/add_interactive_orchestrator.go
+++ b/pkg/cli/add_interactive_orchestrator.go
@@ -135,7 +135,7 @@ func RunAddInteractive(ctx context.Context, workflowSpecs []string, verbose bool
// Step 8: Confirm with user
var secretName, secretValue string
if config.hasWriteAccess && !config.SkipSecret {
- secretName, secretValue, err = config.getSecretInfo()
+ secretName, secretValue, err = config.resolveEngineApiKeyCredential()
if err != nil {
return err
}
@@ -146,7 +146,7 @@ func RunAddInteractive(ctx context.Context, workflowSpecs []string, verbose bool
}
// Step 9: Apply changes (create PR, merge, add secret)
- if err := config.applyChanges(ctx, filesToAdd, initFiles, secretName, secretValue); err != nil {
+ if err := config.createWorkflowPRAndConfigureSecret(ctx, filesToAdd, initFiles, secretName, secretValue); err != nil {
return err
}
diff --git a/pkg/cli/add_interactive_schedule.go b/pkg/cli/add_interactive_schedule.go
index 6d6ce16eb70..9edb9fde84a 100644
--- a/pkg/cli/add_interactive_schedule.go
+++ b/pkg/cli/add_interactive_schedule.go
@@ -9,6 +9,7 @@ import (
"github.com/github/gh-aw/pkg/console"
"github.com/github/gh-aw/pkg/logger"
"github.com/github/gh-aw/pkg/parser"
+ "github.com/github/gh-aw/pkg/sliceutil"
)
var scheduleWizardLog = logger.New("cli:add_interactive_schedule")
@@ -282,7 +283,7 @@ func (c *AddInteractiveConfig) selectScheduleFrequency() error {
// buildScheduleOptions constructs the huh option list for the schedule frequency form.
// The default option (matching the current frequency) is placed first.
func buildScheduleOptions(rawExpr, currentFreq string) []huh.Option[string] {
- var options []huh.Option[string]
+ options := make([]huh.Option[string], 0, len(standardScheduleFrequencies)+1)
// If the current schedule doesn't match any standard frequency, add a "Custom" entry
if currentFreq == "custom" {
@@ -302,16 +303,7 @@ func buildScheduleOptions(rawExpr, currentFreq string) []huh.Option[string] {
// Move the default option to the front so huh selects it initially.
// classifyScheduleFrequency always returns a non-empty string ("custom" as its last resort),
// so currentFreq is never empty when this function is called from selectScheduleFrequency.
- defaultValue := currentFreq
-
- var reordered []huh.Option[string]
- var rest []huh.Option[string]
- for _, opt := range options {
- if opt.Value == defaultValue {
- reordered = append(reordered, opt)
- } else {
- rest = append(rest, opt)
- }
- }
+ reordered := sliceutil.Filter(options, func(opt huh.Option[string]) bool { return opt.Value == currentFreq })
+ rest := sliceutil.Filter(options, func(opt huh.Option[string]) bool { return opt.Value != currentFreq })
return append(reordered, rest...)
}
diff --git a/pkg/cli/add_interactive_schedule_test.go b/pkg/cli/add_interactive_schedule_test.go
index 16ad4dfc7fe..189c4610090 100644
--- a/pkg/cli/add_interactive_schedule_test.go
+++ b/pkg/cli/add_interactive_schedule_test.go
@@ -392,6 +392,12 @@ func TestBuildScheduleOptions(t *testing.T) {
assert.Equal(t, "weekly", opts[0].Value, "weekly should be first option")
})
+ t.Run("3-hourly schedule puts 3-hourly first", func(t *testing.T) {
+ opts := buildScheduleOptions("every 3h", "3-hourly")
+ require.NotEmpty(t, opts, "options should not be empty")
+ assert.Equal(t, "3-hourly", opts[0].Value, "3-hourly should be first option")
+ })
+
t.Run("monthly schedule puts monthly first", func(t *testing.T) {
opts := buildScheduleOptions("0 0 1 * *", "monthly")
require.NotEmpty(t, opts, "options should not be empty")
diff --git a/pkg/cli/add_interactive_secrets.go b/pkg/cli/add_interactive_secrets.go
index e020fd5e9b1..39663846743 100644
--- a/pkg/cli/add_interactive_secrets.go
+++ b/pkg/cli/add_interactive_secrets.go
@@ -5,10 +5,11 @@ import (
"os"
"strings"
+ "github.com/github/gh-aw/pkg/sliceutil"
"github.com/github/gh-aw/pkg/workflow"
)
-// checkExistingSecrets fetches which secrets already exist in the repository
+// checkExistingSecrets fetches which secrets already exist in the repository or its organization
func (c *AddInteractiveConfig) checkExistingSecrets() error {
addInteractiveLog.Print("Checking existing repository secrets")
@@ -19,21 +20,28 @@ func (c *AddInteractiveConfig) checkExistingSecrets() error {
if err != nil {
addInteractiveLog.Printf("Could not fetch existing secrets: %v", err)
// Continue without error - we'll just assume no secrets exist
- return nil
+ } else {
+ for _, name := range parseSecretNames(output) {
+ c.existingSecrets[name] = true
+ addInteractiveLog.Printf("Found existing repository secret: %s", name)
+ }
}
- // Parse the output - each secret name is on its own line
- secretNames := strings.SplitSeq(strings.TrimSpace(string(output)), "\n")
- for name := range secretNames {
- name = strings.TrimSpace(name)
- if name != "" {
- c.existingSecrets[name] = true
- addInteractiveLog.Printf("Found existing secret: %s", name)
+ // Also check org-level secrets if the repo belongs to an organization
+ if org, _, found := strings.Cut(c.RepoOverride, "/"); found && org != "" {
+ orgOutput, orgErr := workflow.RunGH("Checking organization secrets...", "api", fmt.Sprintf("/orgs/%s/actions/secrets", org), "--jq", ".secrets[].name")
+ if orgErr != nil {
+ addInteractiveLog.Printf("Could not fetch org secrets (this is expected for personal repos or if org access is restricted): %v", orgErr)
+ } else {
+ for _, name := range parseSecretNames(orgOutput) {
+ c.existingSecrets[name] = true
+ addInteractiveLog.Printf("Found existing org secret: %s", name)
+ }
}
}
if c.Verbose && len(c.existingSecrets) > 0 {
- fmt.Fprintf(os.Stderr, "Found %d existing repository secret(s)\n", len(c.existingSecrets))
+ fmt.Fprintf(os.Stderr, "Found %d existing secret(s) (repository + organization)\n", len(c.existingSecrets))
}
return nil
@@ -48,9 +56,23 @@ func (c *AddInteractiveConfig) addRepositorySecret(name, value string) error {
return nil
}
-// getSecretInfo returns the secret name and value based on the selected engine
+// parseSecretNames parses newline-delimited GitHub API output and returns the
+// non-empty, trimmed secret names.
+func parseSecretNames(output []byte) []string {
+ trimmed := strings.TrimSpace(string(output))
+ if trimmed == "" {
+ return nil
+ }
+ lines := strings.Split(trimmed, "\n")
+ return sliceutil.Filter(
+ sliceutil.Map(lines, strings.TrimSpace),
+ func(name string) bool { return name != "" },
+ )
+}
+
+// resolveEngineApiKeyCredential returns the secret name and value based on the selected engine
// Returns empty value if the secret already exists in the repository
-func (c *AddInteractiveConfig) getSecretInfo() (name string, value string, err error) {
+func (c *AddInteractiveConfig) resolveEngineApiKeyCredential() (name string, value string, err error) {
addInteractiveLog.Printf("Getting secret info for engine: %s", c.EngineOverride)
secretName, secretValue, existsInRepo, err := GetEngineSecretNameAndValue(c.EngineOverride, c.existingSecrets)
diff --git a/pkg/cli/add_interactive_secrets_test.go b/pkg/cli/add_interactive_secrets_test.go
index 80fe0a05829..f68a9bfec67 100644
--- a/pkg/cli/add_interactive_secrets_test.go
+++ b/pkg/cli/add_interactive_secrets_test.go
@@ -10,7 +10,7 @@ import (
"github.com/stretchr/testify/require"
)
-func TestAddInteractiveConfig_getSecretInfo(t *testing.T) {
+func TestAddInteractiveConfig_resolveEngineApiKeyCredential(t *testing.T) {
tests := []struct {
name string
engineOverride string
@@ -79,7 +79,7 @@ func TestAddInteractiveConfig_getSecretInfo(t *testing.T) {
config.existingSecrets = make(map[string]bool)
}
- name, value, err := config.getSecretInfo()
+ name, value, err := config.resolveEngineApiKeyCredential()
if tt.wantErr {
assert.Error(t, err, "Expected error but got none")
@@ -96,7 +96,7 @@ func TestAddInteractiveConfig_getSecretInfo(t *testing.T) {
}
}
-func TestAddInteractiveConfig_collectAPIKey_noWriteAccess(t *testing.T) {
+func TestAddInteractiveConfig_configureEngineAPISecret_noWriteAccess(t *testing.T) {
tests := []struct {
name string
engine string
@@ -124,15 +124,15 @@ func TestAddInteractiveConfig_collectAPIKey_noWriteAccess(t *testing.T) {
existingSecrets: make(map[string]bool),
}
- // When the user doesn't have write access, collectAPIKey should
+ // When the user doesn't have write access, configureEngineAPISecret should
// return nil without prompting or uploading any secrets.
- err := config.collectAPIKey(tt.engine)
- require.NoError(t, err, "collectAPIKey should succeed without write access")
+ err := config.configureEngineAPISecret(tt.engine)
+ require.NoError(t, err, "configureEngineAPISecret should succeed without write access")
})
}
}
-func TestAddInteractiveConfig_collectAPIKey_skipSecret(t *testing.T) {
+func TestAddInteractiveConfig_configureEngineAPISecret_skipSecret(t *testing.T) {
tests := []struct {
name string
engine string
@@ -161,10 +161,61 @@ func TestAddInteractiveConfig_collectAPIKey_skipSecret(t *testing.T) {
existingSecrets: make(map[string]bool),
}
- // When SkipSecret is true, collectAPIKey should return nil without
+ // When SkipSecret is true, configureEngineAPISecret should return nil without
// prompting or uploading any secrets, even with write access.
- err := config.collectAPIKey(tt.engine)
- require.NoError(t, err, "collectAPIKey should succeed when SkipSecret is true")
+ err := config.configureEngineAPISecret(tt.engine)
+ require.NoError(t, err, "configureEngineAPISecret should succeed when SkipSecret is true")
+ })
+ }
+}
+
+func TestParseSecretNames(t *testing.T) {
+ tests := []struct {
+ name string
+ input []byte
+ expected []string
+ }{
+ {
+ name: "single secret name",
+ input: []byte("MY_SECRET\n"),
+ expected: []string{"MY_SECRET"},
+ },
+ {
+ name: "multiple secret names",
+ input: []byte("SECRET_A\nSECRET_B\nSECRET_C"),
+ expected: []string{"SECRET_A", "SECRET_B", "SECRET_C"},
+ },
+ {
+ name: "empty output",
+ input: []byte(""),
+ expected: nil,
+ },
+ {
+ name: "output with only whitespace",
+ input: []byte(" \n \n"),
+ expected: nil,
+ },
+ {
+ name: "names with surrounding whitespace",
+ input: []byte(" MY_SECRET \n ANOTHER_SECRET "),
+ expected: []string{"MY_SECRET", "ANOTHER_SECRET"},
+ },
+ {
+ name: "output with blank lines interspersed",
+ input: []byte("FIRST_SECRET\n\nSECOND_SECRET\n\n"),
+ expected: []string{"FIRST_SECRET", "SECOND_SECRET"},
+ },
+ {
+ name: "trailing newline is handled correctly",
+ input: []byte("SECRET_ONE\nSECRET_TWO\n"),
+ expected: []string{"SECRET_ONE", "SECRET_TWO"},
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := parseSecretNames(tt.input)
+ assert.Equal(t, tt.expected, result, "parseSecretNames output should match expected")
})
}
}
diff --git a/pkg/cli/add_interactive_workflow.go b/pkg/cli/add_interactive_workflow.go
index afc991e94bd..de2bfc732fc 100644
--- a/pkg/cli/add_interactive_workflow.go
+++ b/pkg/cli/add_interactive_workflow.go
@@ -49,7 +49,7 @@ func (c *AddInteractiveConfig) checkStatusAndOfferRun(ctx context.Context) error
fmt.Fprintf(os.Stderr, "Checking workflow status (attempt %d/5) for: %s\n", i+1, parsed.WorkflowName)
}
// Check if workflow is in status
- statuses, err := getWorkflowStatuses(parsed.WorkflowName, c.RepoOverride, c.Verbose)
+ statuses, err := findWorkflowsByFilenamePattern(parsed.WorkflowName, c.RepoOverride, c.Verbose)
if err != nil {
if c.Verbose {
fmt.Fprintf(os.Stderr, "Status check error: %v\n", err)
@@ -147,9 +147,9 @@ func (c *AddInteractiveConfig) checkStatusAndOfferRun(ctx context.Context) error
return nil
}
-// getWorkflowStatuses is a helper to get workflow statuses for a pattern
+// findWorkflowsByFilenamePattern is a helper to find workflows registered in GitHub by filename pattern.
// The pattern is matched against the workflow filename (basename without extension)
-func getWorkflowStatuses(pattern, repoOverride string, verbose bool) ([]WorkflowStatus, error) {
+func findWorkflowsByFilenamePattern(pattern, repoOverride string, verbose bool) ([]WorkflowStatus, error) {
// This would normally call StatusWorkflows but we need just a simple check
// For now, we'll use the gh CLI directly
// Request 'path' field so we can match by filename, not by workflow name
diff --git a/pkg/cli/add_interactive_workflow_test.go b/pkg/cli/add_interactive_workflow_test.go
index c91bdcfc2aa..e5665c82f8d 100644
--- a/pkg/cli/add_interactive_workflow_test.go
+++ b/pkg/cli/add_interactive_workflow_test.go
@@ -42,7 +42,7 @@ func TestGetWorkflowStatuses(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
// This function calls gh CLI, so it will likely error in tests
// We just verify it doesn't panic
- statuses, err := getWorkflowStatuses(tt.pattern, tt.repoOverride, tt.verbose)
+ statuses, err := findWorkflowsByFilenamePattern(tt.pattern, tt.repoOverride, tt.verbose)
// Either succeeds or fails gracefully, but shouldn't panic
// Note: statuses may be nil even when err is nil (no workflows found)
diff --git a/pkg/cli/add_wildcard_test.go b/pkg/cli/add_wildcard_test.go
index 6526d97b130..fa3fd445402 100644
--- a/pkg/cli/add_wildcard_test.go
+++ b/pkg/cli/add_wildcard_test.go
@@ -299,3 +299,82 @@ on: push
}
}
+
+// TestExpandLocalWildcard tests the expandLocalWildcard function directly,
+// covering the filter (.md only) and map (WorkflowSpec construction) pipeline.
+func TestExpandLocalWildcard(t *testing.T) {
+ tempDir := testutil.TempDir(t, "test-*")
+
+ // Create .md workflow files
+ mdFiles := []string{"alpha.md", "beta.md"}
+ for _, f := range mdFiles {
+ require.NoError(t, os.WriteFile(filepath.Join(tempDir, f), []byte("# Test"), 0644))
+ }
+ // Create non-.md files that must be excluded
+ nonMdFiles := []string{"README.txt", "config.yml", "notes.md.bak"}
+ for _, f := range nonMdFiles {
+ require.NoError(t, os.WriteFile(filepath.Join(tempDir, f), []byte("other"), 0644))
+ }
+
+ origDir, err := os.Getwd()
+ require.NoError(t, err)
+ defer func() { _ = os.Chdir(origDir) }()
+ require.NoError(t, os.Chdir(tempDir))
+
+ t.Run("only .md files are returned", func(t *testing.T) {
+ spec := &WorkflowSpec{
+ RepoSpec: RepoSpec{},
+ WorkflowPath: "./*",
+ WorkflowName: "*",
+ IsWildcard: true,
+ }
+ result, err := expandLocalWildcard(spec)
+ require.NoError(t, err, "should not error for valid glob pattern")
+ require.Len(t, result, 2, "should return exactly the 2 .md files")
+ for _, s := range result {
+ assert.True(t, strings.HasSuffix(s.WorkflowPath, ".md"), "every result path should end in .md: %s", s.WorkflowPath)
+ }
+ })
+
+ t.Run("expanded specs are not wildcards", func(t *testing.T) {
+ spec := &WorkflowSpec{
+ RepoSpec: RepoSpec{},
+ WorkflowPath: "./*.md",
+ WorkflowName: "*",
+ IsWildcard: true,
+ }
+ result, err := expandLocalWildcard(spec)
+ require.NoError(t, err)
+ for _, s := range result {
+ assert.False(t, s.IsWildcard, "expanded spec should not be a wildcard")
+ }
+ })
+
+ t.Run("repo slug and version are inherited from parent spec", func(t *testing.T) {
+ spec := &WorkflowSpec{
+ RepoSpec: RepoSpec{RepoSlug: "owner/repo", Version: "v1.2.3"},
+ WorkflowPath: "./*.md",
+ WorkflowName: "*",
+ IsWildcard: true,
+ }
+ result, err := expandLocalWildcard(spec)
+ require.NoError(t, err)
+ require.NotEmpty(t, result, "should find .md files")
+ for _, s := range result {
+ assert.Equal(t, "owner/repo", s.RepoSlug, "repo slug should be inherited")
+ assert.Equal(t, "v1.2.3", s.Version, "version should be inherited")
+ }
+ })
+
+ t.Run("no matches returns nil without error", func(t *testing.T) {
+ spec := &WorkflowSpec{
+ RepoSpec: RepoSpec{},
+ WorkflowPath: "./*.nonexistent",
+ WorkflowName: "*",
+ IsWildcard: true,
+ }
+ result, err := expandLocalWildcard(spec)
+ require.NoError(t, err, "no matches should not be an error")
+ assert.Nil(t, result, "result should be nil when nothing matches")
+ })
+}
diff --git a/pkg/cli/add_wizard_command.go b/pkg/cli/add_wizard_command.go
index 8f02b12d1e3..e611e366bcb 100644
--- a/pkg/cli/add_wizard_command.go
+++ b/pkg/cli/add_wizard_command.go
@@ -17,7 +17,7 @@ func NewAddWizardCommand(validateEngine func(string) error) *cobra.Command {
cmd := &cobra.Command{
Use: "add-wizard ...",
Short: "Interactively add one or more agentic workflows with guided setup",
- Long: `Interactively add one or more workflows with guided setup.
+ Long: `Interactively add one or more agentic workflows with guided setup.
This command walks you through:
- Selecting an AI engine (Copilot, Claude, or Codex)
diff --git a/pkg/cli/add_workflow_resolution.go b/pkg/cli/add_workflow_resolution.go
index 734a42722f6..646fa26188f 100644
--- a/pkg/cli/add_workflow_resolution.go
+++ b/pkg/cli/add_workflow_resolution.go
@@ -245,25 +245,20 @@ func expandLocalWildcard(spec *WorkflowSpec) ([]*WorkflowSpec, error) {
return nil, nil
}
- var result []*WorkflowSpec
- for _, match := range matches {
- // Only include .md files
- if !strings.HasSuffix(match, ".md") {
- continue
- }
-
- // Create a new spec for each matched file
- workflowName := normalizeWorkflowID(match)
- result = append(result, &WorkflowSpec{
+ mdMatches := sliceutil.Filter(matches, func(m string) bool {
+ return strings.HasSuffix(m, ".md")
+ })
+ result := sliceutil.Map(mdMatches, func(match string) *WorkflowSpec {
+ return &WorkflowSpec{
RepoSpec: RepoSpec{
RepoSlug: spec.RepoSlug,
Version: spec.Version,
},
WorkflowPath: match,
- WorkflowName: workflowName,
+ WorkflowName: normalizeWorkflowID(match),
IsWildcard: false,
- })
- }
+ }
+ })
return result, nil
}
diff --git a/pkg/cli/audit.go b/pkg/cli/audit.go
index 3f23917e0a3..33d1d830ce4 100644
--- a/pkg/cli/audit.go
+++ b/pkg/cli/audit.go
@@ -26,7 +26,7 @@ var auditLog = logger.New("cli:audit")
func NewAuditCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "audit ",
- Short: "Investigate an agentic workflow run and generate a detailed report",
+ Short: "Audit a workflow run and generate a detailed report",
Long: `Audit a single workflow run by downloading artifacts and logs, detecting errors,
analyzing MCP tool usage, and generating a concise Markdown report suitable for AI agents.
diff --git a/pkg/cli/audit_report.go b/pkg/cli/audit_report.go
index 9c4d7bf223e..b591e0822b5 100644
--- a/pkg/cli/audit_report.go
+++ b/pkg/cli/audit_report.go
@@ -339,13 +339,6 @@ func extractDownloadedFiles(logsPath string) []FileInfo {
return files
}
- // Get current working directory to calculate relative paths
- cwd, err := os.Getwd()
- if err != nil {
- auditReportLog.Printf("Failed to get current directory: %v", err)
- cwd = ""
- }
-
for _, entry := range entries {
// Skip directories
if entry.IsDir() {
@@ -355,16 +348,15 @@ func extractDownloadedFiles(logsPath string) []FileInfo {
name := entry.Name()
fullPath := filepath.Join(logsPath, name)
- // Calculate relative path from workspace root (current working directory)
- relativePath := fullPath
- if cwd != "" {
- if relPath, err := filepath.Rel(cwd, fullPath); err == nil {
- relativePath = relPath
- }
+ // Use absolute path so callers get a directly usable path
+ absPath, err := filepath.Abs(fullPath)
+ if err != nil {
+ auditReportLog.Printf("Failed to resolve absolute path for %s: %v", fullPath, err)
+ absPath = fullPath
}
fileInfo := FileInfo{
- Path: relativePath,
+ Path: absPath,
Description: describeFile(name),
}
diff --git a/pkg/cli/audit_report_helpers_test.go b/pkg/cli/audit_report_helpers_test.go
index 49e8f9c5e7a..6e93c090f6b 100644
--- a/pkg/cli/audit_report_helpers_test.go
+++ b/pkg/cli/audit_report_helpers_test.go
@@ -177,10 +177,17 @@ func TestDownloadedFilesInAuditData(t *testing.T) {
t.Errorf("Expected %d files, got %d", expectedCount, len(files))
}
+ // Verify all paths are absolute
+ for _, file := range files {
+ if !filepath.IsAbs(file.Path) {
+ t.Errorf("Expected absolute path, got relative path: %s", file.Path)
+ }
+ }
+
// Verify specific files have correct attributes
fileMap := make(map[string]FileInfo)
for _, file := range files {
- // Use basename for lookup since paths are now relative to workspace root
+ // Use basename for lookup since we only need to find files by name
basename := filepath.Base(file.Path)
fileMap[basename] = file
}
@@ -323,7 +330,7 @@ func TestAuditReportFileListingIntegration(t *testing.T) {
// Build a map for easy lookup
fileMap := make(map[string]FileInfo)
for _, f := range files {
- // Use basename for lookup since paths are now relative to workspace root
+ // Use basename for lookup since we only need to find files by name
basename := filepath.Base(f.Path)
fileMap[basename] = f
}
diff --git a/pkg/cli/dispatch.go b/pkg/cli/dispatch.go
index 50095e76d96..495391dfc21 100644
--- a/pkg/cli/dispatch.go
+++ b/pkg/cli/dispatch.go
@@ -1,6 +1,7 @@
package cli
import (
+ "context"
"fmt"
"os"
"path"
@@ -74,7 +75,7 @@ type fileDownloadFn func(owner, repo, path, ref string) ([]byte, error)
//
// An optional downloader function may be provided as the last argument to override the default
// parser.DownloadFileFromGitHub implementation (used in tests to avoid real network calls).
-func fetchAndSaveRemoteDispatchWorkflows(content string, spec *WorkflowSpec, targetDir string, verbose bool, force bool, tracker *FileTracker, downloaders ...fileDownloadFn) error {
+func fetchAndSaveRemoteDispatchWorkflows(ctx context.Context, content string, spec *WorkflowSpec, targetDir string, verbose bool, force bool, tracker *FileTracker, downloaders ...fileDownloadFn) error {
downloader := fileDownloadFn(parser.DownloadFileFromGitHub)
if len(downloaders) > 0 && downloaders[0] != nil {
downloader = downloaders[0]
@@ -90,7 +91,7 @@ func fetchAndSaveRemoteDispatchWorkflows(content string, spec *WorkflowSpec, tar
owner, repo := parts[0], parts[1]
ref := spec.Version
if ref == "" {
- defaultBranch, err := getRepoDefaultBranch(spec.RepoSlug)
+ defaultBranch, err := getRepoDefaultBranch(ctx, spec.RepoSlug)
if err != nil {
remoteWorkflowLog.Printf("Failed to resolve default branch for %s, falling back to 'main': %v", spec.RepoSlug, err)
ref = "main"
diff --git a/pkg/cli/domains_command.go b/pkg/cli/domains_command.go
new file mode 100644
index 00000000000..c4632272951
--- /dev/null
+++ b/pkg/cli/domains_command.go
@@ -0,0 +1,271 @@
+package cli
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/github/gh-aw/pkg/console"
+ "github.com/github/gh-aw/pkg/constants"
+ "github.com/github/gh-aw/pkg/logger"
+ "github.com/github/gh-aw/pkg/parser"
+ "github.com/github/gh-aw/pkg/workflow"
+ "github.com/spf13/cobra"
+)
+
+var domainsCommandLog = logger.New("cli:domains_command")
+
+// WorkflowDomainsSummary represents a workflow's domain configuration for list output
+type WorkflowDomainsSummary struct {
+ Workflow string `json:"workflow" console:"header:Workflow"`
+ Engine string `json:"engine" console:"header:Engine"`
+ Allowed int `json:"allowed" console:"header:Allowed"`
+ Blocked int `json:"blocked" console:"header:Blocked"`
+}
+
+// WorkflowDomainsDetail represents the detailed domain configuration for a single workflow
+type WorkflowDomainsDetail struct {
+ Workflow string `json:"workflow"`
+ Engine string `json:"engine"`
+ AllowedDomains []string `json:"allowed_domains"`
+ BlockedDomains []string `json:"blocked_domains"`
+}
+
+// DomainItem represents a single domain entry for tabular display
+type DomainItem struct {
+ Domain string `json:"domain" console:"header:Domain"`
+ Ecosystem string `json:"ecosystem" console:"header:Ecosystem"`
+ Status string `json:"status" console:"header:Status"`
+}
+
+// NewDomainsCommand creates the domains command
+func NewDomainsCommand() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "domains [workflow]",
+ Short: "List network domains configured in agentic workflows",
+ Long: `List network domains configured in agentic workflows.
+
+When no workflow is specified, lists all workflows with a summary of their allowed
+and blocked domain counts.
+
+When a workflow ID or file is specified, lists all effective allowed and blocked
+domains for that workflow, including domains expanded from ecosystem identifiers
+(e.g. "node", "python", "github") and engine defaults.
+
+The workflow argument can be:
+- A workflow ID (basename without .md extension, e.g., "weekly-research")
+- A file path (e.g., "weekly-research.md" or ".github/workflows/weekly-research.md")
+
+Examples:
+ ` + string(constants.CLIExtensionPrefix) + ` domains # List all workflows with domain counts
+ ` + string(constants.CLIExtensionPrefix) + ` domains weekly-research # List domains for weekly-research workflow
+ ` + string(constants.CLIExtensionPrefix) + ` domains --json # Output summary in JSON format
+ ` + string(constants.CLIExtensionPrefix) + ` domains weekly-research --json # Output workflow domains in JSON format`,
+ Args: cobra.MaximumNArgs(1),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ jsonFlag, _ := cmd.Flags().GetBool("json")
+
+ if len(args) == 1 {
+ return RunWorkflowDomains(args[0], jsonFlag)
+ }
+ return RunListDomains(jsonFlag)
+ },
+ }
+
+ addJSONFlag(cmd)
+ cmd.ValidArgsFunction = CompleteWorkflowNames
+
+ return cmd
+}
+
+// RunListDomains lists all workflows with their domain configuration summary
+func RunListDomains(jsonOutput bool) error {
+ domainsCommandLog.Printf("Listing domains for all workflows: jsonOutput=%v", jsonOutput)
+
+ workflowsDir := getWorkflowsDir()
+ mdFiles, err := getMarkdownWorkflowFiles(workflowsDir)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, console.FormatErrorMessage(err.Error()))
+ return nil
+ }
+
+ if len(mdFiles) == 0 {
+ if jsonOutput {
+ fmt.Println("[]")
+ return nil
+ }
+ fmt.Fprintln(os.Stderr, console.FormatInfoMessage("No workflow files found."))
+ return nil
+ }
+
+ var summaries []WorkflowDomainsSummary
+
+ for _, file := range mdFiles {
+ name := extractWorkflowNameFromPath(file)
+ engineID, network, tools, runtimes := extractWorkflowDomainConfig(file)
+
+ allowedDomains := computeAllowedDomains(constants.EngineName(engineID), network, tools, runtimes)
+ blockedDomains := workflow.GetBlockedDomains(network)
+
+ summaries = append(summaries, WorkflowDomainsSummary{
+ Workflow: name,
+ Engine: engineID,
+ Allowed: len(allowedDomains),
+ Blocked: len(blockedDomains),
+ })
+ }
+
+ if jsonOutput {
+ jsonBytes, err := json.MarshalIndent(summaries, "", " ")
+ if err != nil {
+ return fmt.Errorf("failed to marshal JSON: %w", err)
+ }
+ fmt.Println(string(jsonBytes))
+ return nil
+ }
+
+ if len(summaries) == 1 {
+ fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("Found 1 workflow"))
+ } else {
+ fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Found %d workflows", len(summaries))))
+ }
+ fmt.Fprint(os.Stderr, console.RenderStruct(summaries))
+
+ return nil
+}
+
+// RunWorkflowDomains lists all effective domains for a specific workflow
+func RunWorkflowDomains(workflowArg string, jsonOutput bool) error {
+ domainsCommandLog.Printf("Listing domains for workflow: %s, jsonOutput=%v", workflowArg, jsonOutput)
+
+ workflowPath, err := ResolveWorkflowPath(workflowArg)
+ if err != nil {
+ return err
+ }
+
+ engineID, network, tools, runtimes := extractWorkflowDomainConfig(workflowPath)
+ name := extractWorkflowNameFromPath(workflowPath)
+
+ allowedDomains := computeAllowedDomains(constants.EngineName(engineID), network, tools, runtimes)
+ blockedDomains := workflow.GetBlockedDomains(network)
+
+ if jsonOutput {
+ detail := WorkflowDomainsDetail{
+ Workflow: name,
+ Engine: engineID,
+ AllowedDomains: allowedDomains,
+ BlockedDomains: blockedDomains,
+ }
+ jsonBytes, err := json.MarshalIndent(detail, "", " ")
+ if err != nil {
+ return fmt.Errorf("failed to marshal JSON: %w", err)
+ }
+ fmt.Println(string(jsonBytes))
+ return nil
+ }
+
+ // Console output: show domain items grouped by allowed/blocked
+ fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(
+ fmt.Sprintf("Network domains for %s (engine: %s)", name, engineID),
+ ))
+
+ items := buildDomainItems(allowedDomains, blockedDomains)
+
+ if len(items) == 0 {
+ fmt.Fprintln(os.Stderr, console.FormatInfoMessage("No domains configured."))
+ return nil
+ }
+
+ // Build table rows
+ headers := []string{"Domain", "Ecosystem", "Status"}
+ rows := make([][]string, 0, len(items))
+ for _, item := range items {
+ rows = append(rows, []string{item.Domain, item.Ecosystem, item.Status})
+ }
+
+ tableConfig := console.TableConfig{
+ Title: "Domains for " + name,
+ Headers: headers,
+ Rows: rows,
+ }
+ fmt.Fprint(os.Stderr, console.RenderTable(tableConfig))
+
+ fmt.Fprintf(os.Stderr, "\n%d allowed, %d blocked\n", len(allowedDomains), len(blockedDomains))
+
+ return nil
+}
+
+// extractWorkflowDomainConfig reads a workflow file and returns its engine ID,
+// network permissions, tools, and runtimes configuration.
+func extractWorkflowDomainConfig(filePath string) (engineID string, network *workflow.NetworkPermissions, tools map[string]any, runtimes map[string]any) {
+ content, err := os.ReadFile(filePath)
+ if err != nil {
+ domainsCommandLog.Printf("Failed to read workflow file %s: %v", filePath, err)
+ return "copilot", nil, nil, nil
+ }
+
+ result, err := parser.ExtractFrontmatterFromContent(string(content))
+ if err != nil || result.Frontmatter == nil {
+ domainsCommandLog.Printf("Failed to parse frontmatter from %s: %v", filePath, err)
+ return "copilot", nil, nil, nil
+ }
+
+ // Reuse the existing engine ID extraction helper which handles both string and object formats
+ engineID = extractEngineIDFromFrontmatter(result.Frontmatter)
+
+ // Parse structured frontmatter config to get NetworkPermissions and runtimes
+ config, err := workflow.ParseFrontmatterConfig(result.Frontmatter)
+ if err != nil {
+ domainsCommandLog.Printf("Failed to parse frontmatter config from %s: %v", filePath, err)
+ return engineID, nil, nil, nil
+ }
+
+ // Extract tools map from raw frontmatter (tools is kept as map[string]any)
+ var toolsMap map[string]any
+ if toolsRaw, ok := result.Frontmatter["tools"]; ok {
+ toolsMap, _ = toolsRaw.(map[string]any)
+ }
+
+ return engineID, config.Network, toolsMap, config.Runtimes
+}
+
+// computeAllowedDomains returns the effective allowed domains for an engine + network config.
+// It mirrors the logic used during workflow compilation.
+func computeAllowedDomains(engine constants.EngineName, network *workflow.NetworkPermissions, tools map[string]any, runtimes map[string]any) []string {
+ combined := workflow.GetAllowedDomainsForEngine(engine, network, tools, runtimes)
+ if combined == "" {
+ return []string{}
+ }
+ // GetAllowedDomainsForEngine returns a comma-separated string; split it
+ parts := strings.Split(combined, ",")
+ result := make([]string, 0, len(parts))
+ for _, p := range parts {
+ if p != "" {
+ result = append(result, p)
+ }
+ }
+ return result
+}
+
+// buildDomainItems creates a list of DomainItem from allowed and blocked domain slices
+func buildDomainItems(allowedDomains, blockedDomains []string) []DomainItem {
+ items := make([]DomainItem, 0, len(allowedDomains)+len(blockedDomains))
+ for _, d := range allowedDomains {
+ ecosystem := workflow.GetDomainEcosystem(d)
+ items = append(items, DomainItem{
+ Domain: d,
+ Ecosystem: ecosystem,
+ Status: "✓ Allowed",
+ })
+ }
+ for _, d := range blockedDomains {
+ ecosystem := workflow.GetDomainEcosystem(d)
+ items = append(items, DomainItem{
+ Domain: d,
+ Ecosystem: ecosystem,
+ Status: "✗ Blocked",
+ })
+ }
+ return items
+}
diff --git a/pkg/cli/domains_command_test.go b/pkg/cli/domains_command_test.go
new file mode 100644
index 00000000000..823bd60eae5
--- /dev/null
+++ b/pkg/cli/domains_command_test.go
@@ -0,0 +1,360 @@
+//go:build !integration
+
+package cli
+
+import (
+ "encoding/json"
+ "io"
+ "os"
+ "path/filepath"
+ "slices"
+ "testing"
+
+ "github.com/github/gh-aw/pkg/workflow"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestRunListDomains_NoWorkflows(t *testing.T) {
+ // Change to a temp directory with no workflows
+ tmpDir := t.TempDir()
+ originalDir, err := os.Getwd()
+ require.NoError(t, err, "Failed to get current directory")
+ err = os.Chdir(tmpDir)
+ require.NoError(t, err, "Failed to change to temp directory")
+ defer os.Chdir(originalDir) //nolint:errcheck
+
+ // Create the .github/workflows directory but with no markdown files
+ err = os.MkdirAll(".github/workflows", 0755)
+ require.NoError(t, err, "Failed to create workflows directory")
+
+ t.Run("no workflows text output", func(t *testing.T) {
+ err := RunListDomains(false)
+ assert.NoError(t, err, "RunListDomains should not error with no workflows")
+ })
+
+ t.Run("no workflows JSON output", func(t *testing.T) {
+ err := RunListDomains(true)
+ assert.NoError(t, err, "RunListDomains should not error with no workflows in JSON mode")
+ })
+}
+
+func TestRunListDomains_WithWorkflow(t *testing.T) {
+ tmpDir := t.TempDir()
+ originalDir, err := os.Getwd()
+ require.NoError(t, err, "Failed to get current directory")
+ err = os.Chdir(tmpDir)
+ require.NoError(t, err, "Failed to change to temp directory")
+ defer os.Chdir(originalDir) //nolint:errcheck
+
+ err = os.MkdirAll(".github/workflows", 0755)
+ require.NoError(t, err, "Failed to create workflows directory")
+
+ // Write a workflow file with network config
+ workflowContent := `---
+engine: copilot
+network:
+ allowed:
+ - github
+ - node
+---
+# Test Workflow
+Do something.
+`
+ workflowPath := filepath.Join(".github", "workflows", "test-workflow.md")
+ err = os.WriteFile(workflowPath, []byte(workflowContent), 0600)
+ require.NoError(t, err, "Failed to write workflow file")
+
+ t.Run("text output", func(t *testing.T) {
+ err := RunListDomains(false)
+ assert.NoError(t, err, "RunListDomains should not error")
+ })
+
+ t.Run("JSON output", func(t *testing.T) {
+ // Capture stdout by redirecting
+ err := RunListDomains(true)
+ assert.NoError(t, err, "RunListDomains JSON should not error")
+ })
+}
+
+func TestRunWorkflowDomains_JSONOutput(t *testing.T) {
+ tmpDir := t.TempDir()
+ originalDir, err := os.Getwd()
+ require.NoError(t, err, "Failed to get current directory")
+ err = os.Chdir(tmpDir)
+ require.NoError(t, err, "Failed to change to temp directory")
+ defer os.Chdir(originalDir) //nolint:errcheck
+
+ err = os.MkdirAll(".github/workflows", 0755)
+ require.NoError(t, err, "Failed to create workflows directory")
+
+ workflowContent := `---
+engine: copilot
+network:
+ allowed:
+ - github
+ blocked:
+ - malicious.example.com
+---
+# Test
+`
+ workflowPath := filepath.Join(".github", "workflows", "my-workflow.md")
+ err = os.WriteFile(workflowPath, []byte(workflowContent), 0600)
+ require.NoError(t, err, "Failed to write workflow file")
+
+ err = RunWorkflowDomains("my-workflow", true)
+ assert.NoError(t, err, "RunWorkflowDomains JSON should not error")
+}
+
+func TestRunWorkflowDomains_TextOutput(t *testing.T) {
+ tmpDir := t.TempDir()
+ originalDir, err := os.Getwd()
+ require.NoError(t, err, "Failed to get current directory")
+ err = os.Chdir(tmpDir)
+ require.NoError(t, err, "Failed to change to temp directory")
+ defer os.Chdir(originalDir) //nolint:errcheck
+
+ err = os.MkdirAll(".github/workflows", 0755)
+ require.NoError(t, err, "Failed to create workflows directory")
+
+ workflowContent := `---
+engine: copilot
+network:
+ allowed:
+ - github
+---
+# Test
+`
+ workflowPath := filepath.Join(".github", "workflows", "my-workflow.md")
+ err = os.WriteFile(workflowPath, []byte(workflowContent), 0600)
+ require.NoError(t, err, "Failed to write workflow file")
+
+ err = RunWorkflowDomains("my-workflow", false)
+ assert.NoError(t, err, "RunWorkflowDomains text should not error")
+}
+
+func TestWorkflowDomainsDetail_JSONMarshaling(t *testing.T) {
+ detail := WorkflowDomainsDetail{
+ Workflow: "my-workflow",
+ Engine: "copilot",
+ AllowedDomains: []string{"api.github.com", "github.com"},
+ BlockedDomains: []string{"malicious.example.com"},
+ }
+
+ jsonBytes, err := json.MarshalIndent(detail, "", " ")
+ require.NoError(t, err, "Should marshal WorkflowDomainsDetail to JSON")
+
+ jsonStr := string(jsonBytes)
+ assert.Contains(t, jsonStr, `"workflow": "my-workflow"`, "JSON should contain workflow name")
+ assert.Contains(t, jsonStr, `"engine": "copilot"`, "JSON should contain engine")
+ assert.Contains(t, jsonStr, `"allowed_domains"`, "JSON should contain allowed_domains key")
+ assert.Contains(t, jsonStr, `"blocked_domains"`, "JSON should contain blocked_domains key")
+ assert.Contains(t, jsonStr, `"api.github.com"`, "JSON should contain allowed domain")
+ assert.Contains(t, jsonStr, `"malicious.example.com"`, "JSON should contain blocked domain")
+}
+
+func TestWorkflowDomainsSummary_JSONMarshaling(t *testing.T) {
+ summary := WorkflowDomainsSummary{
+ Workflow: "my-workflow",
+ Engine: "copilot",
+ Allowed: 10,
+ Blocked: 2,
+ }
+
+ jsonBytes, err := json.MarshalIndent(summary, "", " ")
+ require.NoError(t, err, "Should marshal WorkflowDomainsSummary to JSON")
+
+ jsonStr := string(jsonBytes)
+ assert.Contains(t, jsonStr, `"workflow": "my-workflow"`, "JSON should contain workflow name")
+ assert.Contains(t, jsonStr, `"engine": "copilot"`, "JSON should contain engine")
+ assert.Contains(t, jsonStr, `"allowed": 10`, "JSON should contain allowed count")
+ assert.Contains(t, jsonStr, `"blocked": 2`, "JSON should contain blocked count")
+}
+
+func TestBuildDomainItems(t *testing.T) {
+ allowed := []string{"api.github.com", "github.com"}
+ blocked := []string{"malicious.example.com"}
+
+ items := buildDomainItems(allowed, blocked)
+ require.Len(t, items, 3, "Should have 3 items total")
+
+ // First two should be allowed
+ assert.Equal(t, "api.github.com", items[0].Domain, "First item should be api.github.com")
+ assert.Contains(t, items[0].Status, "Allowed", "First item should be allowed")
+
+ assert.Equal(t, "github.com", items[1].Domain, "Second item should be github.com")
+ assert.Contains(t, items[1].Status, "Allowed", "Second item should be allowed")
+
+ // Last one should be blocked
+ assert.Equal(t, "malicious.example.com", items[2].Domain, "Third item should be malicious.example.com")
+ assert.Contains(t, items[2].Status, "Blocked", "Third item should be blocked")
+}
+
+func TestBuildDomainItems_EcosystemAnnotation(t *testing.T) {
+ allowed := []string{"registry.npmjs.org", "pypi.org"}
+ items := buildDomainItems(allowed, nil)
+
+ require.Len(t, items, 2, "Should have 2 items")
+
+ // registry.npmjs.org should be in the node ecosystem
+ assert.Equal(t, "registry.npmjs.org", items[0].Domain, "First domain should be registry.npmjs.org")
+ assert.Equal(t, "node", items[0].Ecosystem, "registry.npmjs.org should be in node ecosystem")
+
+ // pypi.org should be in the python ecosystem
+ assert.Equal(t, "pypi.org", items[1].Domain, "Second domain should be pypi.org")
+ assert.Equal(t, "python", items[1].Ecosystem, "pypi.org should be in python ecosystem")
+}
+
+func TestNewDomainsCommand(t *testing.T) {
+ cmd := NewDomainsCommand()
+ assert.NotNil(t, cmd, "NewDomainsCommand should return a command")
+ assert.Equal(t, "domains [workflow]", cmd.Use, "Command use should be 'domains [workflow]'")
+ assert.NotEmpty(t, cmd.Short, "Command should have a short description")
+ assert.NotEmpty(t, cmd.Long, "Command should have a long description")
+
+ // Check --json flag exists
+ jsonFlag := cmd.Flags().Lookup("json")
+ assert.NotNil(t, jsonFlag, "Command should have --json flag")
+
+ // Check max 1 argument
+ err := cmd.Args(cmd, []string{"a", "b"})
+ require.Error(t, err, "Command should reject more than 1 argument")
+
+ err = cmd.Args(cmd, []string{})
+ require.NoError(t, err, "Command should accept 0 arguments")
+
+ err = cmd.Args(cmd, []string{"workflow-name"})
+ require.NoError(t, err, "Command should accept 1 argument")
+}
+
+func TestExtractWorkflowDomainConfig(t *testing.T) {
+ tmpDir := t.TempDir()
+
+ t.Run("workflow with network config", func(t *testing.T) {
+ content := `---
+engine: claude
+network:
+ allowed:
+ - github
+ - python
+ blocked:
+ - bad.example.com
+---
+# Test
+`
+ path := filepath.Join(tmpDir, "test.md")
+ err := os.WriteFile(path, []byte(content), 0600)
+ require.NoError(t, err, "Failed to write test file")
+
+ engineID, network, _, _ := extractWorkflowDomainConfig(path)
+ assert.Equal(t, "claude", engineID, "Engine should be claude")
+ require.NotNil(t, network, "Network should not be nil")
+ assert.Equal(t, []string{"github", "python"}, network.Allowed, "Allowed should match")
+ assert.Equal(t, []string{"bad.example.com"}, network.Blocked, "Blocked should match")
+ })
+
+ t.Run("workflow without network config defaults to copilot", func(t *testing.T) {
+ content := `---
+engine: copilot
+---
+# Test
+`
+ path := filepath.Join(tmpDir, "no-network.md")
+ err := os.WriteFile(path, []byte(content), 0600)
+ require.NoError(t, err, "Failed to write test file")
+
+ engineID, network, _, _ := extractWorkflowDomainConfig(path)
+ assert.Equal(t, "copilot", engineID, "Engine should be copilot")
+ assert.Nil(t, network, "Network should be nil when not configured")
+ })
+
+ t.Run("nonexistent file defaults to copilot", func(t *testing.T) {
+ engineID, network, _, _ := extractWorkflowDomainConfig("/nonexistent/file.md")
+ assert.Equal(t, "copilot", engineID, "Engine should default to copilot")
+ assert.Nil(t, network, "Network should be nil for nonexistent file")
+ })
+}
+
+func TestComputeAllowedDomains(t *testing.T) {
+ t.Run("copilot engine without network config", func(t *testing.T) {
+ domains := computeAllowedDomains("copilot", nil, nil, nil)
+ // Copilot has default domains
+ assert.NotEmpty(t, domains, "Should have default Copilot domains")
+ assert.True(t, slices.Contains(domains, "api.github.com"), "Should contain api.github.com")
+ })
+
+ t.Run("returns empty for unknown engine with empty network", func(t *testing.T) {
+ network := &workflow.NetworkPermissions{
+ Allowed: []string{},
+ }
+ domains := computeAllowedDomains("custom", network, nil, nil)
+ assert.Empty(t, domains, "Should return empty for explicit empty allowed list")
+ })
+}
+
+func TestRunListDomains_RepoRoot(t *testing.T) {
+ originalDir, err := os.Getwd()
+ require.NoError(t, err, "Failed to get current directory")
+
+ repoRoot := filepath.Join(originalDir, "..", "..")
+ err = os.Chdir(repoRoot)
+ require.NoError(t, err, "Failed to change to repository root")
+ defer os.Chdir(originalDir) //nolint:errcheck
+
+ t.Run("JSON output from repo root", func(t *testing.T) {
+ err := RunListDomains(true)
+ assert.NoError(t, err, "RunListDomains JSON should not error from repo root")
+ })
+
+ t.Run("text output from repo root", func(t *testing.T) {
+ err := RunListDomains(false)
+ assert.NoError(t, err, "RunListDomains text should not error from repo root")
+ })
+}
+
+func TestRunListDomains_JSONFormat(t *testing.T) {
+ tmpDir := t.TempDir()
+ originalDir, err := os.Getwd()
+ require.NoError(t, err, "Failed to get current directory")
+ err = os.Chdir(tmpDir)
+ require.NoError(t, err, "Failed to change to temp directory")
+ defer os.Chdir(originalDir) //nolint:errcheck
+
+ err = os.MkdirAll(".github/workflows", 0755)
+ require.NoError(t, err, "Failed to create workflows directory")
+
+ // Write two workflow files
+ for _, wf := range []struct{ name, engine string }{
+ {"wf-a", "copilot"},
+ {"wf-b", "claude"},
+ } {
+ content := "---\nengine: " + wf.engine + "\n---\n# Test\n"
+ path := filepath.Join(".github", "workflows", wf.name+".md")
+ err = os.WriteFile(path, []byte(content), 0600)
+ require.NoError(t, err, "Failed to write workflow file")
+ }
+
+ // Capture JSON by redirecting stdout
+ oldStdout := os.Stdout
+ r, w, err := os.Pipe()
+ require.NoError(t, err, "Failed to create pipe")
+ os.Stdout = w
+
+ err = RunListDomains(true)
+ require.NoError(t, err, "RunListDomains should not error")
+
+ w.Close()
+ os.Stdout = oldStdout
+
+ outputBytes, err := io.ReadAll(r)
+ require.NoError(t, err, "Failed to read pipe output")
+ r.Close()
+
+ output := string(outputBytes)
+ assert.NotEmpty(t, output, "JSON output should not be empty")
+
+ var summaries []WorkflowDomainsSummary
+ err = json.Unmarshal(outputBytes, &summaries)
+ require.NoError(t, err, "JSON output should be valid JSON array")
+ assert.Len(t, summaries, 2, "Should have 2 workflow summaries")
+}
diff --git a/pkg/cli/fetch.go b/pkg/cli/fetch.go
index 13fcfe53eb8..94fe710eeca 100644
--- a/pkg/cli/fetch.go
+++ b/pkg/cli/fetch.go
@@ -83,7 +83,7 @@ func fetchRemoteWorkflow(spec *WorkflowSpec, verbose bool) (*FetchedWorkflow, er
}
// Resolve the ref to a commit SHA for source tracking
- commitSHA, err := parser.ResolveRefToSHA(owner, repo, ref)
+ commitSHA, err := parser.ResolveRefToSHAForHost(owner, repo, ref, spec.Host)
if err != nil {
remoteWorkflowLog.Printf("Failed to resolve ref to SHA: %v", err)
// Continue without SHA - we can still fetch the content
@@ -96,7 +96,7 @@ func fetchRemoteWorkflow(spec *WorkflowSpec, verbose bool) (*FetchedWorkflow, er
}
// Download the workflow file from GitHub
- content, err := parser.DownloadFileFromGitHub(owner, repo, spec.WorkflowPath, ref)
+ content, err := parser.DownloadFileFromGitHubForHost(owner, repo, spec.WorkflowPath, ref, spec.Host)
if err != nil {
// Try with common workflow directory prefixes if the direct path fails.
// This handles short workflow names without path separators (e.g. "my-workflow.md").
@@ -107,7 +107,7 @@ func fetchRemoteWorkflow(spec *WorkflowSpec, verbose bool) (*FetchedWorkflow, er
altPath += ".md"
}
remoteWorkflowLog.Printf("Direct path failed, trying: %s", altPath)
- if altContent, altErr := parser.DownloadFileFromGitHub(owner, repo, altPath, ref); altErr == nil {
+ if altContent, altErr := parser.DownloadFileFromGitHubForHost(owner, repo, altPath, ref, spec.Host); altErr == nil {
return &FetchedWorkflow{
Content: altContent,
CommitSHA: commitSHA,
diff --git a/pkg/cli/gateway_logs.go b/pkg/cli/gateway_logs.go
index 5d73452d2a4..cd17e14295a 100644
--- a/pkg/cli/gateway_logs.go
+++ b/pkg/cli/gateway_logs.go
@@ -19,13 +19,13 @@ import (
"os"
"path/filepath"
"sort"
+ "strconv"
"strings"
"time"
"github.com/github/gh-aw/pkg/console"
"github.com/github/gh-aw/pkg/logger"
"github.com/github/gh-aw/pkg/sliceutil"
- "github.com/github/gh-aw/pkg/stringutil"
"github.com/github/gh-aw/pkg/timeutil"
)
@@ -514,30 +514,30 @@ func renderGatewayMetricsTable(metrics *GatewayMetrics, verbose bool) string {
// Server metrics table
if len(metrics.Servers) > 0 {
- output.WriteString("Server Usage:\n")
- output.WriteString("┌────────────────────────────┬──────────┬────────────┬───────────┬────────┐\n")
- output.WriteString("│ Server │ Requests │ Tool Calls │ Avg Time │ Errors │\n")
- output.WriteString("├────────────────────────────┼──────────┼────────────┼───────────┼────────┤\n")
-
// Sort servers by request count
serverNames := getSortedServerNames(metrics)
+ serverRows := make([][]string, 0, len(serverNames))
for _, serverName := range serverNames {
server := metrics.Servers[serverName]
avgTime := 0.0
if server.RequestCount > 0 {
avgTime = server.TotalDuration / float64(server.RequestCount)
}
-
- fmt.Fprintf(&output, "│ %-26s │ %8d │ %10d │ %7.0fms │ %6d │\n",
- stringutil.Truncate(serverName, 26),
- server.RequestCount,
- server.ToolCallCount,
- avgTime,
- server.ErrorCount)
+ serverRows = append(serverRows, []string{
+ serverName,
+ strconv.Itoa(server.RequestCount),
+ strconv.Itoa(server.ToolCallCount),
+ fmt.Sprintf("%.0fms", avgTime),
+ strconv.Itoa(server.ErrorCount),
+ })
}
- output.WriteString("└────────────────────────────┴──────────┴────────────┴───────────┴────────┘\n")
+ output.WriteString(console.RenderTable(console.TableConfig{
+ Title: "Server Usage",
+ Headers: []string{"Server", "Requests", "Tool Calls", "Avg Time", "Errors"},
+ Rows: serverRows,
+ }))
}
// Tool metrics table (if verbose)
@@ -551,28 +551,29 @@ func renderGatewayMetricsTable(metrics *GatewayMetrics, verbose bool) string {
continue
}
- fmt.Fprintf(&output, "\n%s:\n", serverName)
- output.WriteString("┌──────────────────────────┬───────┬──────────┬──────────┬──────────┐\n")
- output.WriteString("│ Tool │ Calls │ Avg Time │ Max Time │ Errors │\n")
- output.WriteString("├──────────────────────────┼───────┼──────────┼──────────┼──────────┤\n")
-
// Sort tools by call count
toolNames := sliceutil.MapToSlice(server.Tools)
sort.Slice(toolNames, func(i, j int) bool {
return server.Tools[toolNames[i]].CallCount > server.Tools[toolNames[j]].CallCount
})
+ toolRows := make([][]string, 0, len(toolNames))
for _, toolName := range toolNames {
tool := server.Tools[toolName]
- fmt.Fprintf(&output, "│ %-24s │ %5d │ %6.0fms │ %6.0fms │ %8d │\n",
- stringutil.Truncate(toolName, 24),
- tool.CallCount,
- tool.AvgDuration,
- tool.MaxDuration,
- tool.ErrorCount)
- }
-
- output.WriteString("└──────────────────────────┴───────┴──────────┴──────────┴──────────┘\n")
+ toolRows = append(toolRows, []string{
+ toolName,
+ strconv.Itoa(tool.CallCount),
+ fmt.Sprintf("%.0fms", tool.AvgDuration),
+ fmt.Sprintf("%.0fms", tool.MaxDuration),
+ strconv.Itoa(tool.ErrorCount),
+ })
+ }
+
+ output.WriteString(console.RenderTable(console.TableConfig{
+ Title: serverName,
+ Headers: []string{"Tool", "Calls", "Avg Time", "Max Time", "Errors"},
+ Rows: toolRows,
+ }))
}
}
diff --git a/pkg/cli/git.go b/pkg/cli/git.go
index 8b4f9fbf195..0ca4c85b406 100644
--- a/pkg/cli/git.go
+++ b/pkg/cli/git.go
@@ -141,48 +141,6 @@ func getHostFromOriginRemote() string {
return host
}
-// isGHESInstance returns true if the git remote origin points to a GitHub Enterprise Server instance
-// (not github.com). This is used to enable GHES-specific behavior like auto-configuring api-target
-// and network firewall domains.
-func isGHESInstance() bool {
- host := getHostFromOriginRemote()
- isGHES := host != "github.com"
- gitLog.Printf("GHES instance check: host=%s, isGHES=%v", host, isGHES)
- return isGHES
-}
-
-// getGHESAPIURL returns the API URL for a GHES instance (e.g., "https://ghes.example.com/api/v3")
-// or an empty string if not a GHES instance. This is used to auto-populate engine.api-target.
-func getGHESAPIURL() string {
- host := getHostFromOriginRemote()
- if host == "github.com" {
- return ""
- }
-
- // GHES API URL format: https:///api/v3
- apiURL := fmt.Sprintf("https://%s/api/v3", host)
- gitLog.Printf("GHES API URL: %s", apiURL)
- return apiURL
-}
-
-// getGHESAllowedDomains returns a list of domains that should be added to the firewall
-// allowed list for GHES instances. This includes the GHES hostname and api..
-func getGHESAllowedDomains() []string {
- host := getHostFromOriginRemote()
- if host == "github.com" {
- return nil
- }
-
- // For GHES, allow both the main host and api subdomain
- domains := []string{
- host,
- "api." + host,
- }
-
- gitLog.Printf("GHES allowed domains: %v", domains)
- return domains
-}
-
// getRepositorySlugFromRemote extracts the repository slug (owner/repo) from git remote URL
func getRepositorySlugFromRemote() string {
gitLog.Print("Getting repository slug from git remote")
diff --git a/pkg/cli/git_ghes_test.go b/pkg/cli/git_ghes_test.go
deleted file mode 100644
index 04795ab76b8..00000000000
--- a/pkg/cli/git_ghes_test.go
+++ /dev/null
@@ -1,166 +0,0 @@
-//go:build !integration
-
-package cli
-
-import (
- "os"
- "os/exec"
- "testing"
-
- "github.com/github/gh-aw/pkg/testutil"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestIsGHESInstance(t *testing.T) {
- tests := []struct {
- name string
- remoteURL string
- wantIsGHES bool
- }{
- {
- name: "public GitHub",
- remoteURL: "https://github.com/org/repo.git",
- wantIsGHES: false,
- },
- {
- name: "GHES instance",
- remoteURL: "https://ghes.example.com/org/repo.git",
- wantIsGHES: true,
- },
- {
- name: "GHES SSH format",
- remoteURL: "git@ghes.example.com:org/repo.git",
- wantIsGHES: true,
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- tmpDir := testutil.TempDir(t, "test-*")
- originalDir, err := os.Getwd()
- require.NoError(t, err, "Failed to get current directory")
- defer func() {
- _ = os.Chdir(originalDir)
- }()
-
- require.NoError(t, os.Chdir(tmpDir), "Failed to change to temp directory")
-
- // Initialize git repo
- require.NoError(t, exec.Command("git", "init").Run(), "Failed to init git repo")
- exec.Command("git", "config", "user.name", "Test User").Run()
- exec.Command("git", "config", "user.email", "test@example.com").Run()
-
- // Set remote URL
- require.NoError(t, exec.Command("git", "remote", "add", "origin", tt.remoteURL).Run(), "Failed to add remote")
- defer func() { _ = exec.Command("git", "remote", "remove", "origin").Run() }()
-
- got := isGHESInstance()
- assert.Equal(t, tt.wantIsGHES, got, "isGHESInstance() returned unexpected result")
- })
- }
-}
-
-func TestGetGHESAPIURL(t *testing.T) {
- tests := []struct {
- name string
- remoteURL string
- wantAPIURL string
- }{
- {
- name: "public GitHub returns empty",
- remoteURL: "https://github.com/org/repo.git",
- wantAPIURL: "",
- },
- {
- name: "GHES instance returns API URL",
- remoteURL: "https://ghes.example.com/org/repo.git",
- wantAPIURL: "https://ghes.example.com/api/v3",
- },
- {
- name: "GHES SSH format returns API URL",
- remoteURL: "git@contoso.ghe.com:org/repo.git",
- wantAPIURL: "https://contoso.ghe.com/api/v3",
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- tmpDir := testutil.TempDir(t, "test-*")
- originalDir, err := os.Getwd()
- require.NoError(t, err, "Failed to get current directory")
- defer func() {
- _ = os.Chdir(originalDir)
- }()
-
- require.NoError(t, os.Chdir(tmpDir), "Failed to change to temp directory")
-
- // Initialize git repo
- require.NoError(t, exec.Command("git", "init").Run(), "Failed to init git repo")
- exec.Command("git", "config", "user.name", "Test User").Run()
- exec.Command("git", "config", "user.email", "test@example.com").Run()
-
- // Set remote URL
- require.NoError(t, exec.Command("git", "remote", "add", "origin", tt.remoteURL).Run(), "Failed to add remote")
- defer func() { _ = exec.Command("git", "remote", "remove", "origin").Run() }()
-
- got := getGHESAPIURL()
- assert.Equal(t, tt.wantAPIURL, got, "getGHESAPIURL() returned unexpected result")
- })
- }
-}
-
-func TestGetGHESAllowedDomains(t *testing.T) {
- tests := []struct {
- name string
- remoteURL string
- wantDomains []string
- }{
- {
- name: "public GitHub returns nil",
- remoteURL: "https://github.com/org/repo.git",
- wantDomains: nil,
- },
- {
- name: "GHES instance returns host and api subdomain",
- remoteURL: "https://ghes.example.com/org/repo.git",
- wantDomains: []string{
- "ghes.example.com",
- "api.ghes.example.com",
- },
- },
- {
- name: "GHES SSH format returns domains",
- remoteURL: "git@contoso-aw.ghe.com:org/repo.git",
- wantDomains: []string{
- "contoso-aw.ghe.com",
- "api.contoso-aw.ghe.com",
- },
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- tmpDir := testutil.TempDir(t, "test-*")
- originalDir, err := os.Getwd()
- require.NoError(t, err, "Failed to get current directory")
- defer func() {
- _ = os.Chdir(originalDir)
- }()
-
- require.NoError(t, os.Chdir(tmpDir), "Failed to change to temp directory")
-
- // Initialize git repo
- require.NoError(t, exec.Command("git", "init").Run(), "Failed to init git repo")
- exec.Command("git", "config", "user.name", "Test User").Run()
- exec.Command("git", "config", "user.email", "test@example.com").Run()
-
- // Set remote URL
- require.NoError(t, exec.Command("git", "remote", "add", "origin", tt.remoteURL).Run(), "Failed to add remote")
- defer func() { _ = exec.Command("git", "remote", "remove", "origin").Run() }()
-
- got := getGHESAllowedDomains()
- assert.Equal(t, tt.wantDomains, got, "getGHESAllowedDomains() returned unexpected result")
- })
- }
-}
diff --git a/pkg/cli/hash_command.go b/pkg/cli/hash_command.go
index bc42896e029..01cce3e81ce 100644
--- a/pkg/cli/hash_command.go
+++ b/pkg/cli/hash_command.go
@@ -16,7 +16,7 @@ var hashLog = logger.New("cli:hash")
func NewHashCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "hash-frontmatter ",
- Short: "Compute frontmatter hash for a workflow",
+ Short: "Compute the frontmatter hash for a workflow",
Long: `Compute a deterministic SHA-256 hash of workflow frontmatter.
The hash includes:
diff --git a/pkg/cli/includes.go b/pkg/cli/includes.go
index 905e2cfaa4e..d01a0daa609 100644
--- a/pkg/cli/includes.go
+++ b/pkg/cli/includes.go
@@ -2,6 +2,7 @@ package cli
import (
"bufio"
+ "context"
"errors"
"fmt"
"os"
@@ -119,6 +120,8 @@ func fetchAndSaveRemoteFrontmatterImports(content string, spec *WorkflowSpec, ta
return nil
}
+ remoteWorkflowLog.Printf("Fetching frontmatter imports for workflow: repo=%s, path=%s", spec.RepoSlug, spec.WorkflowPath)
+
parts := strings.SplitN(spec.RepoSlug, "/", 2)
if len(parts) != 2 {
return nil
@@ -127,7 +130,7 @@ func fetchAndSaveRemoteFrontmatterImports(content string, spec *WorkflowSpec, ta
ref := spec.Version
if ref == "" {
// Resolve the actual default branch of the source repo rather than assuming "main"
- defaultBranch, err := getRepoDefaultBranch(spec.RepoSlug)
+ defaultBranch, err := getRepoDefaultBranch(context.Background(), spec.RepoSlug)
if err != nil {
remoteWorkflowLog.Printf("Failed to resolve default branch for %s, falling back to 'main': %v", spec.RepoSlug, err)
ref = "main"
@@ -189,6 +192,8 @@ func fetchFrontmatterImportsRecursive(content, owner, repo, ref, currentBaseDir,
return
}
+ remoteWorkflowLog.Printf("Processing %d frontmatter imports recursively: owner=%s, repo=%s, ref=%s", len(importPaths), owner, repo, ref)
+
// Pre-compute the absolute target directory once for path-traversal boundary checks.
absTargetDir, err := filepath.Abs(targetDir)
if err != nil {
diff --git a/pkg/cli/interactive.go b/pkg/cli/interactive.go
index e25e0f4f741..a0f2f00b2cc 100644
--- a/pkg/cli/interactive.go
+++ b/pkg/cli/interactive.go
@@ -122,7 +122,7 @@ func (b *InteractiveWorkflowBuilder) promptForConfiguration() error {
huh.NewOption("copilot - GitHub Copilot CLI", "copilot"),
huh.NewOption("claude - Anthropic Claude Code coding agent", "claude"),
huh.NewOption("codex - OpenAI Codex engine", "codex"),
- huh.NewOption("custom - Custom engine configuration", "custom"),
+ huh.NewOption("gemini - Google Gemini CLI", "gemini"),
}
// Prepare tool options
@@ -141,6 +141,7 @@ func (b *InteractiveWorkflowBuilder) promptForConfiguration() error {
// Pre-detect network access based on repo contents
detectedNetworks := detectNetworkFromRepo()
+ interactiveLog.Printf("Pre-detected networks from repo: %v", detectedNetworks)
// Prepare network options
networkOptions := []huh.Option[string]{
@@ -231,6 +232,8 @@ func (b *InteractiveWorkflowBuilder) promptForConfiguration() error {
b.Tools = selectedTools
b.SafeOutputs = selectedOutputs
+ interactiveLog.Printf("User configuration selected: trigger=%s, engine=%s, tools=%v, safe_outputs=%v", b.Trigger, b.Engine, selectedTools, selectedOutputs)
+
return nil
}
diff --git a/pkg/cli/list_workflows_command.go b/pkg/cli/list_workflows_command.go
index e5684796bcd..c158564e643 100644
--- a/pkg/cli/list_workflows_command.go
+++ b/pkg/cli/list_workflows_command.go
@@ -243,6 +243,7 @@ func RunListWorkflows(repo, path, pattern string, verbose bool, jsonOutput bool,
// getRemoteWorkflowFiles fetches the list of workflow files from a remote repository
func getRemoteWorkflowFiles(repoSpec, workflowPath string, verbose bool, jsonOutput bool) ([]string, error) {
+ listWorkflowsLog.Printf("Fetching remote workflow files: repoSpec=%s, path=%s", repoSpec, workflowPath)
// Parse repo spec: owner/repo[@ref]
var owner, repo, ref string
parts := strings.SplitN(repoSpec, "@", 2)
@@ -266,10 +267,13 @@ func getRemoteWorkflowFiles(repoSpec, workflowPath string, verbose bool, jsonOut
}
// Use the parser package to list workflow files
+ listWorkflowsLog.Printf("Listing remote workflow files: owner=%s, repo=%s, ref=%s, path=%s", owner, repo, ref, workflowPath)
files, err := parser.ListWorkflowFiles(owner, repo, ref, workflowPath)
if err != nil {
+ listWorkflowsLog.Printf("Failed to list remote workflow files: %v", err)
return nil, fmt.Errorf("failed to list workflow files from %s/%s: %w", owner, repo, err)
}
+ listWorkflowsLog.Printf("Found %d remote workflow files", len(files))
return files, nil
}
diff --git a/pkg/cli/logs_download.go b/pkg/cli/logs_download.go
index 95708bab223..962582ea337 100644
--- a/pkg/cli/logs_download.go
+++ b/pkg/cli/logs_download.go
@@ -145,55 +145,23 @@ func findArtifactDir(outputDir, baseName string, legacyName string) string {
return ""
}
-// flattenUnifiedArtifact flattens the unified agent artifact directory structure.
-// The artifact is uploaded with all paths under /tmp/gh-aw/, so the action strips the
-// common prefix and files land directly inside the artifact directory (new structure).
-// For backward compatibility, it also handles the old structure where the full
-// tmp/gh-aw/ path was preserved inside the artifact directory.
-// New artifact name: "agent" (preferred)
-// Legacy artifact name: "agent-artifacts" (backward compat for older workflow runs)
-// In workflow_call context, the artifact may be prefixed: "-agent"
-func flattenUnifiedArtifact(outputDir string, verbose bool) error {
- agentArtifactsDir := findArtifactDir(outputDir, "agent", "agent-artifacts")
- if agentArtifactsDir == "" {
- // No unified artifact, nothing to flatten
- return nil
- }
-
- logsDownloadLog.Printf("Flattening unified agent artifact directory: %s", agentArtifactsDir)
-
- // Check for old nested structure (agent-artifacts/tmp/gh-aw/)
- tmpGhAwPath := filepath.Join(agentArtifactsDir, "tmp", "gh-aw")
- hasOldStructure := false
- if _, err := os.Stat(tmpGhAwPath); err == nil {
- hasOldStructure = true
- logsDownloadLog.Printf("Found old artifact structure with tmp/gh-aw prefix")
- }
-
- // Determine the source path for flattening
- var sourcePath string
- if hasOldStructure {
- // Old structure: flatten from agent-artifacts/tmp/gh-aw/
- sourcePath = tmpGhAwPath
- } else {
- // New structure: flatten from artifact directory directly
- sourcePath = agentArtifactsDir
- logsDownloadLog.Printf("Found new artifact structure without tmp/gh-aw prefix")
- }
-
- // Walk through source path and move all files to root output directory
- err := filepath.Walk(sourcePath, func(path string, info os.FileInfo, err error) error {
+// flattenArtifactTree moves all files from sourceDir into outputDir, preserving relative paths,
+// then removes artifactDir (which may equal sourceDir, or be a parent of it in the old-structure
+// case). label is used in log and user-facing messages.
+// Cleanup failures are non-fatal: they are logged (and optionally printed) but do not return an error.
+func flattenArtifactTree(sourceDir, artifactDir, outputDir, label string, verbose bool) error {
+ err := filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// Skip the source directory itself
- if path == sourcePath {
+ if path == sourceDir {
return nil
}
// Calculate relative path from source
- relPath, err := filepath.Rel(sourcePath, path)
+ relPath, err := filepath.Rel(sourceDir, path)
if err != nil {
return fmt.Errorf("failed to get relative path for %s: %w", path, err)
}
@@ -207,7 +175,6 @@ func flattenUnifiedArtifact(outputDir string, verbose bool) error {
}
logsDownloadLog.Printf("Created directory: %s", destPath)
} else {
- // Move file to destination
// Ensure parent directory exists with owner+group permissions only (0750)
if err := os.MkdirAll(filepath.Dir(destPath), 0750); err != nil {
return fmt.Errorf("failed to create parent directory for %s: %w", destPath, err)
@@ -226,34 +193,62 @@ func flattenUnifiedArtifact(outputDir string, verbose bool) error {
})
if err != nil {
- return fmt.Errorf("failed to flatten unified artifact: %w", err)
+ return fmt.Errorf("failed to flatten %s: %w", label, err)
}
- // Remove the now-empty artifact directory structure
- if err := os.RemoveAll(agentArtifactsDir); err != nil {
- logsDownloadLog.Printf("Failed to remove agent artifact directory %s: %v", agentArtifactsDir, err)
+ // Remove the now-empty artifact directory structure.
+ // Don't fail the entire operation if cleanup fails.
+ if err := os.RemoveAll(artifactDir); err != nil {
+ logsDownloadLog.Printf("Failed to remove %s directory %s: %v", label, artifactDir, err)
if verbose {
- fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to remove agent artifact directory: %v", err)))
+ fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to remove %s directory: %v", label, err)))
}
- // Don't fail the entire operation if cleanup fails
} else {
- logsDownloadLog.Printf("Removed agent artifact directory: %s", agentArtifactsDir)
+ logsDownloadLog.Printf("Removed %s directory: %s", label, artifactDir)
if verbose {
- fmt.Fprintln(os.Stderr, console.FormatVerboseMessage("Flattened unified agent artifact and removed nested structure"))
+ fmt.Fprintln(os.Stderr, console.FormatVerboseMessage(fmt.Sprintf("Flattened %s and removed nested structure", label)))
}
}
return nil
}
-// flattenActivationArtifact flattens the activation artifact directory structure
-// The activation artifact contains aw_info.json and aw-prompts/prompt.txt
+// flattenUnifiedArtifact flattens the unified agent artifact directory structure.
+// The artifact is uploaded with all paths under /tmp/gh-aw/, so the action strips the
+// common prefix and files land directly inside the artifact directory (new structure).
+// For backward compatibility, it also handles the old structure where the full
+// tmp/gh-aw/ path was preserved inside the artifact directory.
+// New artifact name: "agent" (preferred)
+// Legacy artifact name: "agent-artifacts" (backward compat for older workflow runs)
+// In workflow_call context, the artifact may be prefixed: "-agent"
+func flattenUnifiedArtifact(outputDir string, verbose bool) error {
+ agentArtifactsDir := findArtifactDir(outputDir, "agent", "agent-artifacts")
+ if agentArtifactsDir == "" {
+ // No unified artifact, nothing to flatten
+ return nil
+ }
+
+ logsDownloadLog.Printf("Flattening unified agent artifact directory: %s", agentArtifactsDir)
+
+ // Determine the source path: old structure preserves the tmp/gh-aw/ prefix inside the artifact
+ sourceDir := agentArtifactsDir
+ tmpGhAwPath := filepath.Join(agentArtifactsDir, "tmp", "gh-aw")
+ if _, err := os.Stat(tmpGhAwPath); err == nil {
+ logsDownloadLog.Printf("Found old artifact structure with tmp/gh-aw prefix")
+ sourceDir = tmpGhAwPath
+ } else {
+ logsDownloadLog.Printf("Found new artifact structure without tmp/gh-aw prefix")
+ }
+
+ return flattenArtifactTree(sourceDir, agentArtifactsDir, outputDir, "unified agent artifact", verbose)
+}
+
+// flattenActivationArtifact flattens the activation artifact directory structure.
+// The activation artifact contains aw_info.json and aw-prompts/prompt.txt.
// This function moves those files to the root output directory and removes the nested structure.
// In workflow_call context, the artifact may be prefixed: "-activation"
func flattenActivationArtifact(outputDir string, verbose bool) error {
activationDir := findArtifactDir(outputDir, "activation", "")
-
- // Check if activation directory exists
if activationDir == "" {
// No activation artifact, nothing to flatten
return nil
@@ -261,73 +256,12 @@ func flattenActivationArtifact(outputDir string, verbose bool) error {
logsDownloadLog.Printf("Flattening activation artifact directory: %s", activationDir)
- // Walk through activation directory and move all files to root output directory
- err := filepath.Walk(activationDir, func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
-
- // Skip the source directory itself
- if path == activationDir {
- return nil
- }
-
- // Calculate relative path from source
- relPath, err := filepath.Rel(activationDir, path)
- if err != nil {
- return fmt.Errorf("failed to get relative path for %s: %w", path, err)
- }
-
- destPath := filepath.Join(outputDir, relPath)
-
- if info.IsDir() {
- // Create directory in destination with owner+group permissions only (0750)
- if err := os.MkdirAll(destPath, 0750); err != nil {
- return fmt.Errorf("failed to create directory %s: %w", destPath, err)
- }
- logsDownloadLog.Printf("Created directory: %s", destPath)
- } else {
- // Move file to destination
- // Ensure parent directory exists with owner+group permissions only (0750)
- if err := os.MkdirAll(filepath.Dir(destPath), 0750); err != nil {
- return fmt.Errorf("failed to create parent directory for %s: %w", destPath, err)
- }
-
- if err := os.Rename(path, destPath); err != nil {
- return fmt.Errorf("failed to move file %s to %s: %w", path, destPath, err)
- }
- logsDownloadLog.Printf("Moved file: %s → %s", path, destPath)
- if verbose {
- fmt.Fprintln(os.Stderr, console.FormatVerboseMessage(fmt.Sprintf("Flattened: %s → %s", relPath, relPath)))
- }
- }
-
- return nil
- })
-
- if err != nil {
- return fmt.Errorf("failed to flatten activation artifact: %w", err)
- }
-
- // Remove the now-empty activation directory structure
- if err := os.RemoveAll(activationDir); err != nil {
- logsDownloadLog.Printf("Failed to remove activation directory %s: %v", activationDir, err)
- if verbose {
- fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to remove activation directory: %v", err)))
- }
- // Don't fail the entire operation if cleanup fails
- } else {
- logsDownloadLog.Printf("Removed activation directory: %s", activationDir)
- if verbose {
- fmt.Fprintln(os.Stderr, console.FormatVerboseMessage("Flattened activation artifact and removed nested structure"))
- }
- }
-
- return nil
+ return flattenArtifactTree(activationDir, activationDir, outputDir, "activation artifact", verbose)
}
+// flattenAgentOutputsArtifact flattens the agent_outputs artifact directory structure.
// The agent_outputs artifact contains session logs with detailed token usage data
-// that are critical for accurate token count parsing
+// that are critical for accurate token count parsing.
func flattenAgentOutputsArtifact(outputDir string, verbose bool) error {
agentOutputsDir := filepath.Join(outputDir, "agent_outputs")
@@ -340,69 +274,7 @@ func flattenAgentOutputsArtifact(outputDir string, verbose bool) error {
logsDownloadLog.Printf("Flattening agent_outputs directory: %s", agentOutputsDir)
- // Walk through agent_outputs and move all files to root output directory
- err := filepath.Walk(agentOutputsDir, func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
-
- // Skip the root directory itself
- if path == agentOutputsDir {
- return nil
- }
-
- // Calculate relative path from agent_outputs
- relPath, err := filepath.Rel(agentOutputsDir, path)
- if err != nil {
- return fmt.Errorf("failed to get relative path for %s: %w", path, err)
- }
-
- destPath := filepath.Join(outputDir, relPath)
-
- if info.IsDir() {
- // Create directory in destination
- if err := os.MkdirAll(destPath, 0750); err != nil {
- return fmt.Errorf("failed to create directory %s: %w", destPath, err)
- }
- logsDownloadLog.Printf("Created directory: %s", destPath)
- } else {
- // Move file to destination
- // Ensure parent directory exists
- if err := os.MkdirAll(filepath.Dir(destPath), 0750); err != nil {
- return fmt.Errorf("failed to create parent directory for %s: %w", destPath, err)
- }
-
- if err := os.Rename(path, destPath); err != nil {
- return fmt.Errorf("failed to move file %s to %s: %w", path, destPath, err)
- }
- logsDownloadLog.Printf("Moved file: %s → %s", path, destPath)
- if verbose {
- fmt.Fprintln(os.Stderr, console.FormatVerboseMessage(fmt.Sprintf("Flattened: %s → %s", relPath, relPath)))
- }
- }
-
- return nil
- })
-
- if err != nil {
- return fmt.Errorf("failed to flatten agent_outputs artifact: %w", err)
- }
-
- // Remove the now-empty agent_outputs directory
- if err := os.RemoveAll(agentOutputsDir); err != nil {
- logsDownloadLog.Printf("Failed to remove agent_outputs directory %s: %v", agentOutputsDir, err)
- if verbose {
- fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to remove agent_outputs directory: %v", err)))
- }
- // Don't fail the entire operation if cleanup fails
- } else {
- logsDownloadLog.Printf("Removed agent_outputs directory: %s", agentOutputsDir)
- if verbose {
- fmt.Fprintln(os.Stderr, console.FormatVerboseMessage("Flattened agent_outputs artifact and removed nested structure"))
- }
- }
-
- return nil
+ return flattenArtifactTree(agentOutputsDir, agentOutputsDir, outputDir, "agent_outputs artifact", verbose)
}
// downloadWorkflowRunLogs downloads and unzips workflow run logs using GitHub API
diff --git a/pkg/cli/logs_flatten_test.go b/pkg/cli/logs_flatten_test.go
index 95d8f659e5e..8d0c36fcc52 100644
--- a/pkg/cli/logs_flatten_test.go
+++ b/pkg/cli/logs_flatten_test.go
@@ -10,11 +10,10 @@ import (
"testing"
"github.com/github/gh-aw/pkg/testutil"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
-// Tests for artifact unfold rule implementation
-// Unfold rule: If an artifact download folder contains a single file, move the file to root and delete the folder
-
func TestFlattenSingleFileArtifacts(t *testing.T) {
tests := []struct {
name string
@@ -532,3 +531,108 @@ func TestFlattenUnifiedArtifact(t *testing.T) {
})
}
}
+
+// TestFlattenArtifactTree tests the shared flatten helper directly.
+
+func TestFlattenArtifactTreeBasic(t *testing.T) {
+ outputDir := t.TempDir()
+ artifactDir := filepath.Join(outputDir, "myartifact")
+ require.NoError(t, os.MkdirAll(artifactDir, 0750), "setup: create artifactDir")
+ require.NoError(t, os.WriteFile(filepath.Join(artifactDir, "file.txt"), []byte("hello"), 0600), "setup: write file")
+
+ err := flattenArtifactTree(artifactDir, artifactDir, outputDir, "test artifact", false)
+ require.NoError(t, err, "flattenArtifactTree should succeed")
+
+ content, err := os.ReadFile(filepath.Join(outputDir, "file.txt"))
+ require.NoError(t, err, "file should be moved to outputDir")
+ assert.Equal(t, "hello", string(content), "file content should be preserved")
+
+ _, err = os.Stat(artifactDir)
+ assert.True(t, os.IsNotExist(err), "artifactDir should be removed after flattening")
+}
+
+func TestFlattenArtifactTreeNestedDirs(t *testing.T) {
+ outputDir := t.TempDir()
+ artifactDir := filepath.Join(outputDir, "artifact")
+ nestedDir := filepath.Join(artifactDir, "subdir", "deeper")
+ require.NoError(t, os.MkdirAll(nestedDir, 0750), "setup: create nested dirs")
+ require.NoError(t, os.WriteFile(filepath.Join(nestedDir, "nested.txt"), []byte("nested"), 0600), "setup: write nested file")
+
+ err := flattenArtifactTree(artifactDir, artifactDir, outputDir, "nested artifact", false)
+ require.NoError(t, err, "flattenArtifactTree should succeed with nested dirs")
+
+ content, err := os.ReadFile(filepath.Join(outputDir, "subdir", "deeper", "nested.txt"))
+ require.NoError(t, err, "nested file should be moved preserving relative path")
+ assert.Equal(t, "nested", string(content), "nested file content should be preserved")
+
+ _, err = os.Stat(artifactDir)
+ assert.True(t, os.IsNotExist(err), "artifactDir should be removed")
+}
+
+func TestFlattenArtifactTreeDifferentSourceAndArtifactDir(t *testing.T) {
+ // Covers the old-structure unified artifact case where sourceDir is a subdirectory of artifactDir.
+ outputDir := t.TempDir()
+ artifactDir := filepath.Join(outputDir, "agent-artifacts")
+ sourceDir := filepath.Join(artifactDir, "tmp", "gh-aw")
+ require.NoError(t, os.MkdirAll(sourceDir, 0750), "setup: create old-structure sourceDir")
+ require.NoError(t, os.WriteFile(filepath.Join(sourceDir, "session.json"), []byte(`{}`), 0600), "setup: write file")
+
+ err := flattenArtifactTree(sourceDir, artifactDir, outputDir, "unified agent artifact", false)
+ require.NoError(t, err, "flattenArtifactTree should succeed")
+
+ content, err := os.ReadFile(filepath.Join(outputDir, "session.json"))
+ require.NoError(t, err, "file should be moved to outputDir relative to sourceDir")
+ assert.Equal(t, `{}`, string(content), "file content should be preserved")
+
+ _, err = os.Stat(artifactDir)
+ assert.True(t, os.IsNotExist(err), "entire artifactDir should be removed, not just sourceDir")
+}
+
+func TestFlattenArtifactTreeEmptySource(t *testing.T) {
+ outputDir := t.TempDir()
+ artifactDir := filepath.Join(outputDir, "empty-artifact")
+ require.NoError(t, os.MkdirAll(artifactDir, 0750), "setup: create empty artifactDir")
+
+ err := flattenArtifactTree(artifactDir, artifactDir, outputDir, "empty artifact", false)
+ require.NoError(t, err, "flattenArtifactTree should succeed on empty dir")
+
+ _, err = os.Stat(artifactDir)
+ assert.True(t, os.IsNotExist(err), "empty artifactDir should be removed")
+}
+
+func TestFlattenArtifactTreeMultipleFiles(t *testing.T) {
+ outputDir := t.TempDir()
+ artifactDir := filepath.Join(outputDir, "multi")
+ require.NoError(t, os.MkdirAll(artifactDir, 0750), "setup: create artifactDir")
+
+ files := map[string]string{
+ "a.txt": "content-a",
+ "b.txt": "content-b",
+ "c.txt": "content-c",
+ }
+ for name, body := range files {
+ require.NoError(t, os.WriteFile(filepath.Join(artifactDir, name), []byte(body), 0600), "setup: write "+name)
+ }
+
+ err := flattenArtifactTree(artifactDir, artifactDir, outputDir, "multi artifact", false)
+ require.NoError(t, err, "flattenArtifactTree should succeed")
+
+ for name, expected := range files {
+ content, err := os.ReadFile(filepath.Join(outputDir, name))
+ require.NoError(t, err, "file %s should exist in outputDir", name)
+ assert.Equal(t, expected, string(content), "content of %s should be preserved", name)
+ }
+}
+
+func TestFlattenArtifactTreeVerboseMode(t *testing.T) {
+ outputDir := t.TempDir()
+ artifactDir := filepath.Join(outputDir, "verbose-artifact")
+ require.NoError(t, os.MkdirAll(artifactDir, 0750), "setup: create artifactDir")
+ require.NoError(t, os.WriteFile(filepath.Join(artifactDir, "log.txt"), []byte("log"), 0600), "setup: write file")
+
+ err := flattenArtifactTree(artifactDir, artifactDir, outputDir, "verbose artifact", true)
+ require.NoError(t, err, "flattenArtifactTree should succeed in verbose mode")
+
+ _, err = os.ReadFile(filepath.Join(outputDir, "log.txt"))
+ require.NoError(t, err, "file should be moved in verbose mode too")
+}
diff --git a/pkg/cli/remote_workflow_test.go b/pkg/cli/remote_workflow_test.go
index 0685f434be9..d9df711905f 100644
--- a/pkg/cli/remote_workflow_test.go
+++ b/pkg/cli/remote_workflow_test.go
@@ -3,6 +3,7 @@
package cli
import (
+ "context"
"errors"
"fmt"
"os"
@@ -677,7 +678,7 @@ engine: copilot
}
tmpDir := t.TempDir()
- err := fetchAndSaveRemoteDispatchWorkflows(content, spec, tmpDir, false, false, nil)
+ err := fetchAndSaveRemoteDispatchWorkflows(context.Background(), content, spec, tmpDir, false, false, nil)
require.NoError(t, err, "should not error when no safe-outputs present")
entries, readErr := os.ReadDir(tmpDir)
@@ -705,7 +706,7 @@ safe-outputs:
}
tmpDir := t.TempDir()
- err := fetchAndSaveRemoteDispatchWorkflows(content, spec, tmpDir, false, false, nil)
+ err := fetchAndSaveRemoteDispatchWorkflows(context.Background(), content, spec, tmpDir, false, false, nil)
require.NoError(t, err, "should not error for local workflow")
entries, readErr := os.ReadDir(tmpDir)
@@ -735,7 +736,7 @@ safe-outputs:
}
tmpDir := t.TempDir()
- err := fetchAndSaveRemoteDispatchWorkflows(content, spec, tmpDir, false, false, nil)
+ err := fetchAndSaveRemoteDispatchWorkflows(context.Background(), content, spec, tmpDir, false, false, nil)
require.NoError(t, err, "should not error when all workflow names are macros")
entries, readErr := os.ReadDir(tmpDir)
@@ -774,7 +775,7 @@ safe-outputs:
WorkflowPath: ".github/workflows/my-workflow.md",
}
- err := fetchAndSaveRemoteDispatchWorkflows(content, spec, tmpDir, false, false, nil)
+ err := fetchAndSaveRemoteDispatchWorkflows(context.Background(), content, spec, tmpDir, false, false, nil)
require.NoError(t, err)
// The existing file must be untouched (no network call attempted because file already exists)
@@ -818,7 +819,7 @@ safe-outputs:
WorkflowPath: ".github/workflows/my-workflow.md",
}
- err := fetchAndSaveRemoteDispatchWorkflows(content, spec, tmpDir, false, false, tracker)
+ err := fetchAndSaveRemoteDispatchWorkflows(context.Background(), content, spec, tmpDir, false, false, tracker)
require.NoError(t, err)
assert.Empty(t, tracker.CreatedFiles, "pre-existing file must not appear in CreatedFiles")
assert.Empty(t, tracker.ModifiedFiles, "pre-existing file must not appear in ModifiedFiles")
@@ -843,7 +844,7 @@ safe-outputs:
}
tmpDir := t.TempDir()
- err := fetchAndSaveRemoteDispatchWorkflows(content, spec, tmpDir, false, false, nil)
+ err := fetchAndSaveRemoteDispatchWorkflows(context.Background(), content, spec, tmpDir, false, false, nil)
require.NoError(t, err, "invalid RepoSlug should return nil without error")
entries, readErr := os.ReadDir(tmpDir)
@@ -1489,7 +1490,7 @@ safe-outputs:
WorkflowPath: ".github/workflows/main.md",
}
- err := fetchAndSaveRemoteDispatchWorkflows(content, spec, workflowsDir, false, false, nil)
+ err := fetchAndSaveRemoteDispatchWorkflows(context.Background(), content, spec, workflowsDir, false, false, nil)
require.Error(t, err, "should error when existing file has a different source repo")
assert.Contains(t, err.Error(), "target-workflow", "error should name the conflicting file")
assert.Contains(t, err.Error(), "otherorg/other-repo", "error should mention existing source")
@@ -1524,7 +1525,7 @@ safe-outputs:
WorkflowPath: ".github/workflows/main.md",
}
- err := fetchAndSaveRemoteDispatchWorkflows(content, spec, workflowsDir, false, false, nil)
+ err := fetchAndSaveRemoteDispatchWorkflows(context.Background(), content, spec, workflowsDir, false, false, nil)
require.NoError(t, err, "should not error when existing file is from the same source repo")
// File must not have been modified
@@ -1556,7 +1557,7 @@ safe-outputs:
WorkflowPath: ".github/workflows/main.md",
}
- err := fetchAndSaveRemoteDispatchWorkflows(content, spec, workflowsDir, false, false, nil)
+ err := fetchAndSaveRemoteDispatchWorkflows(context.Background(), content, spec, workflowsDir, false, false, nil)
require.Error(t, err, "should error when existing file has no source field")
assert.Contains(t, err.Error(), "target-workflow", "error should name the conflicting file")
assert.Contains(t, err.Error(), "(no source field)", "error should show placeholder for missing source")
@@ -1595,7 +1596,7 @@ safe-outputs:
mockDownloader := func(_, _, _, _ string) ([]byte, error) {
return nil, errors.New("download not available in unit tests")
}
- err := fetchAndSaveRemoteDispatchWorkflows(content, spec, dir, false, true, nil, mockDownloader)
+ err := fetchAndSaveRemoteDispatchWorkflows(context.Background(), content, spec, dir, false, true, nil, mockDownloader)
assert.NoError(t, err, "force=true should bypass conflict detection and return nil (download fails silently)")
}
diff --git a/pkg/cli/remove_command.go b/pkg/cli/remove_command.go
index d6a59ac1234..71410c9d12c 100644
--- a/pkg/cli/remove_command.go
+++ b/pkg/cli/remove_command.go
@@ -1,7 +1,6 @@
package cli
import (
- "bufio"
"fmt"
"os"
"path/filepath"
@@ -391,9 +390,7 @@ func findIncludesInContent(content, baseDir string, verbose bool) ([]string, err
_ = verbose // unused parameter for now, keeping for potential future use
var includes []string
- scanner := bufio.NewScanner(strings.NewReader(content))
- for scanner.Scan() {
- line := scanner.Text()
+ for line := range strings.Lines(content) {
directive := parser.ParseImportDirective(line)
if directive != nil {
includePath := directive.Path
@@ -411,5 +408,5 @@ func findIncludesInContent(content, baseDir string, verbose bool) ([]string, err
}
}
- return includes, scanner.Err()
+ return includes, nil // strings.Lines iterates over an in-memory string; no I/O errors can occur.
}
diff --git a/pkg/cli/resources.go b/pkg/cli/resources.go
index 06c57cd292f..7f71512ed29 100644
--- a/pkg/cli/resources.go
+++ b/pkg/cli/resources.go
@@ -1,6 +1,7 @@
package cli
import (
+ "context"
"fmt"
"os"
"path"
@@ -75,7 +76,7 @@ func fetchAndSaveRemoteResources(content string, spec *WorkflowSpec, targetDir s
owner, repo := parts[0], parts[1]
ref := spec.Version
if ref == "" {
- defaultBranch, err := getRepoDefaultBranch(spec.RepoSlug)
+ defaultBranch, err := getRepoDefaultBranch(context.Background(), spec.RepoSlug)
if err != nil {
remoteWorkflowLog.Printf("Failed to resolve default branch for %s, falling back to 'main': %v", spec.RepoSlug, err)
ref = "main"
diff --git a/pkg/cli/run_push.go b/pkg/cli/run_push.go
index a995c1d1cc9..9bd9d06e9cf 100644
--- a/pkg/cli/run_push.go
+++ b/pkg/cli/run_push.go
@@ -16,6 +16,7 @@ import (
"github.com/github/gh-aw/pkg/fileutil"
"github.com/github/gh-aw/pkg/logger"
"github.com/github/gh-aw/pkg/parser"
+ "github.com/github/gh-aw/pkg/sliceutil"
"github.com/github/gh-aw/pkg/workflow"
)
@@ -115,10 +116,7 @@ func collectWorkflowFiles(ctx context.Context, workflowPath string, verbose bool
runPushLog.Printf("Import collection completed")
// Convert map to slice
- var result []string
- for file := range files {
- result = append(result, file)
- }
+ result := sliceutil.MapToSlice(files)
// Sort files for stable output
sort.Strings(result)
diff --git a/pkg/cli/secrets_command.go b/pkg/cli/secrets_command.go
index 0b1d72f5af5..853beceeac5 100644
--- a/pkg/cli/secrets_command.go
+++ b/pkg/cli/secrets_command.go
@@ -12,7 +12,7 @@ func NewSecretsCommand() *cobra.Command {
secretsCommandLog.Print("Creating secrets command with subcommands")
cmd := &cobra.Command{
Use: "secrets",
- Short: "Manage repository secrets",
+ Short: "Manage GitHub Actions secrets for agentic workflows",
Long: `Manage GitHub Actions secrets for GitHub Agentic Workflows.
This command provides tools for managing secrets required by agentic workflows, including
diff --git a/pkg/cli/secrets_command_test.go b/pkg/cli/secrets_command_test.go
index c26d9c94ee4..c5a7371fc7c 100644
--- a/pkg/cli/secrets_command_test.go
+++ b/pkg/cli/secrets_command_test.go
@@ -14,7 +14,7 @@ func TestNewSecretsCommand(t *testing.T) {
require.NotNil(t, cmd, "NewSecretsCommand should not return nil")
assert.Equal(t, "secrets", cmd.Use, "Command use should be 'secrets'")
- assert.Equal(t, "Manage repository secrets", cmd.Short, "Command short description should match")
+ assert.Equal(t, "Manage GitHub Actions secrets for agentic workflows", cmd.Short, "Command short description should match")
assert.Contains(t, cmd.Long, "Manage GitHub Actions secrets", "Command long description should contain expected text")
// Verify subcommands are added
diff --git a/pkg/cli/spec.go b/pkg/cli/spec.go
index 5a5a158a4ec..02172136ebd 100644
--- a/pkg/cli/spec.go
+++ b/pkg/cli/spec.go
@@ -32,6 +32,7 @@ type WorkflowSpec struct {
WorkflowPath string // e.g., "workflows/workflow-name.md"
WorkflowName string // e.g., "workflow-name"
IsWildcard bool // true if this is a wildcard spec (e.g., "owner/repo/*")
+ Host string // explicit hostname from URL (e.g., "github.com", "myorg.ghe.com"); empty = use configured GH_HOST
}
// isLocalWorkflowPath checks if a path refers to a local filesystem workflow.
@@ -148,19 +149,21 @@ func parseRepoSpec(repoSpec string) (*RepoSpec, error) {
// - https://raw.githubusercontent.com/owner/repo/refs/heads/branch/path/to/workflow.md
// - https://raw.githubusercontent.com/owner/repo/COMMIT_SHA/path/to/workflow.md
// - https://raw.githubusercontent.com/owner/repo/refs/tags/tag/path/to/workflow.md
+// - https://myorg.ghe.com/owner/repo/blob/branch/path/to/workflow.md (GHE)
func parseGitHubURL(spec string) (*WorkflowSpec, error) {
specLog.Printf("Parsing GitHub URL: %s", spec)
- // First validate that this is a GitHub URL (github.com or raw.githubusercontent.com)
parsedURL, err := url.Parse(spec)
if err != nil {
specLog.Printf("Failed to parse URL: %v", err)
return nil, fmt.Errorf("invalid URL: %w", err)
}
- // Must be a GitHub URL
- if parsedURL.Host != "github.com" && parsedURL.Host != "raw.githubusercontent.com" {
- specLog.Printf("Invalid host: %s", parsedURL.Host)
- return nil, errors.New("URL must be from github.com or raw.githubusercontent.com")
+ if parsedURL.Host == "" {
+ return nil, fmt.Errorf("URL must include a host: %s", spec)
+ }
+
+ if !isGitHubHost(parsedURL.Host) {
+ return nil, fmt.Errorf("URL must be from github.com or a GitHub Enterprise host (*.ghe.com), got %q", parsedURL.Host)
}
owner, repo, ref, filePath, err := parser.ParseRepoFileURL(spec)
@@ -169,7 +172,7 @@ func parseGitHubURL(spec string) (*WorkflowSpec, error) {
return nil, err
}
- specLog.Printf("Parsed GitHub URL: owner=%s, repo=%s, ref=%s, path=%s", owner, repo, ref, filePath)
+ specLog.Printf("Parsed GitHub URL: owner=%s, repo=%s, ref=%s, path=%s, host=%s", owner, repo, ref, filePath, parsedURL.Host)
// Ensure the file path ends with .md
if !strings.HasSuffix(filePath, ".md") {
@@ -181,6 +184,13 @@ func parseGitHubURL(spec string) (*WorkflowSpec, error) {
return nil, fmt.Errorf("invalid GitHub URL: '%s/%s' does not look like a valid GitHub repository", owner, repo)
}
+ // For raw.githubusercontent.com content, the API host is github.com.
+ // For all other hosts (github.com, GHE), use the URL's host as-is.
+ host := parsedURL.Host
+ if host == "raw.githubusercontent.com" {
+ host = "github.com"
+ }
+
return &WorkflowSpec{
RepoSpec: RepoSpec{
RepoSlug: fmt.Sprintf("%s/%s", owner, repo),
@@ -188,6 +198,7 @@ func parseGitHubURL(spec string) (*WorkflowSpec, error) {
},
WorkflowPath: filePath,
WorkflowName: normalizeWorkflowID(filePath),
+ Host: host,
}, nil
}
@@ -195,6 +206,15 @@ func parseGitHubURL(spec string) (*WorkflowSpec, error) {
// Format: owner/repo/workflows/workflow-name[@version] or owner/repo/workflow-name[@version]
// Also supports full GitHub URLs like https://github.com/owner/repo/blob/branch/path/to/workflow.md
// Also supports local paths like ./workflows/workflow-name.md
+
+// isGitHubHost returns true if the given host is a recognized GitHub or GitHub Enterprise host:
+// github.com, raw.githubusercontent.com, or any *.ghe.com host.
+func isGitHubHost(host string) bool {
+ return host == "github.com" ||
+ host == "raw.githubusercontent.com" ||
+ strings.HasSuffix(host, ".ghe.com") ||
+ strings.HasSuffix(host, ".github.com")
+}
func parseWorkflowSpec(spec string) (*WorkflowSpec, error) {
specLog.Printf("Parsing workflow spec: %q", spec)
diff --git a/pkg/cli/spec_test.go b/pkg/cli/spec_test.go
index 1831f7da036..fb920ab0aa1 100644
--- a/pkg/cli/spec_test.go
+++ b/pkg/cli/spec_test.go
@@ -132,6 +132,7 @@ func TestParseWorkflowSpec(t *testing.T) {
wantWorkflowPath string
wantWorkflowName string
wantVersion string
+ wantHost string
wantErr bool
errContains string
}{
@@ -142,6 +143,7 @@ func TestParseWorkflowSpec(t *testing.T) {
wantWorkflowPath: "workflows/release-issue-linker.md",
wantWorkflowName: "release-issue-linker",
wantVersion: "main",
+ wantHost: "github.com",
wantErr: false,
},
{
@@ -181,10 +183,20 @@ func TestParseWorkflowSpec(t *testing.T) {
wantErr: false,
},
{
- name: "GitHub URL - invalid domain",
+ name: "GitHub URL - GHE.com instance",
+ spec: "https://myorg.ghe.com/owner/repo/blob/main/workflows/test.md",
+ wantRepo: "owner/repo",
+ wantWorkflowPath: "workflows/test.md",
+ wantWorkflowName: "test",
+ wantVersion: "main",
+ wantHost: "myorg.ghe.com",
+ wantErr: false,
+ },
+ {
+ name: "GitHub URL - non-github.com host is rejected (e.g. gitlab.com)",
spec: "https://gitlab.com/owner/repo/blob/main/workflows/test.md",
wantErr: true,
- errContains: "must be from github.com",
+ errContains: "github.com",
},
{
name: "GitHub URL - missing file extension",
@@ -328,6 +340,9 @@ func TestParseWorkflowSpec(t *testing.T) {
if spec.Version != tt.wantVersion {
t.Errorf("parseWorkflowSpec() version = %q, want %q", spec.Version, tt.wantVersion)
}
+ if tt.wantHost != "" && spec.Host != tt.wantHost {
+ t.Errorf("parseWorkflowSpec() host = %q, want %q", spec.Host, tt.wantHost)
+ }
})
}
}
diff --git a/pkg/cli/trial_command.go b/pkg/cli/trial_command.go
index 540a3ee033a..abe2f67cc27 100644
--- a/pkg/cli/trial_command.go
+++ b/pkg/cli/trial_command.go
@@ -181,7 +181,7 @@ Trial results are saved both locally (in trials/ directory) and in the host repo
cmd.Flags().String("clone-repo", "", "Alternative to --logical-repo: clone the contents of the specified repo into the host repo instead of using logical repository simulation")
cmd.Flags().String("host-repo", "", "Custom host repository slug (defaults to '/gh-aw-trial'). Use '.' for current repository")
- cmd.Flags().String("repo", "", "Alias for --host-repo")
+ cmd.Flags().String("repo", "", "Alias for --host-repo: the repository where workflows are installed and run (note: different semantics from --repo in other commands)")
cmd.Flags().Bool("delete-host-repo-after", false, "Delete the host repository after completion (default: keep)")
cmd.Flags().Bool("force-delete-host-repo-before", false, "Force delete the host repository before creation, if it exists before creating it")
cmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompts")
diff --git a/pkg/cli/update_command.go b/pkg/cli/update_command.go
index ba470c3a727..9abc5f6c151 100644
--- a/pkg/cli/update_command.go
+++ b/pkg/cli/update_command.go
@@ -17,7 +17,7 @@ func NewUpdateCommand(validateEngine func(string) error) *cobra.Command {
cmd := &cobra.Command{
Use: "update [workflow]...",
Short: "Update agentic workflows from their source repositories",
- Long: `Update one or more workflows from their source repositories.
+ Long: `Update one or more agentic workflows from their source repositories.
The update command fetches the latest version of each workflow from its source
repository, merges upstream changes with any local modifications, and recompiles.
diff --git a/pkg/cli/update_integration_test.go b/pkg/cli/update_integration_test.go
index d35200227c2..eb5f83173bc 100644
--- a/pkg/cli/update_integration_test.go
+++ b/pkg/cli/update_integration_test.go
@@ -3,6 +3,7 @@
package cli
import (
+ "context"
"os"
"os/exec"
"path/filepath"
@@ -359,7 +360,7 @@ func TestIsCommitSHA_Integration(t *testing.T) {
func TestGetRepoDefaultBranch_Integration(t *testing.T) {
skipWithoutGitHubAuth(t)
- branch, err := getRepoDefaultBranch("actions/checkout")
+ branch, err := getRepoDefaultBranch(context.Background(), "actions/checkout")
require.NoError(t, err, "Should fetch default branch for actions/checkout")
assert.Equal(t, "main", branch, "actions/checkout default branch should be 'main'")
}
diff --git a/pkg/cli/update_workflows.go b/pkg/cli/update_workflows.go
index e49dcc2d806..e297df67499 100644
--- a/pkg/cli/update_workflows.go
+++ b/pkg/cli/update_workflows.go
@@ -1,6 +1,7 @@
package cli
import (
+ "context"
"errors"
"fmt"
"net/url"
@@ -202,7 +203,7 @@ func resolveLatestRef(repo, currentRef string, allowMajor, verbose bool) (string
// logically track the default branch.
func resolveLatestCommitFromDefaultBranch(repo, currentSHA string, verbose bool) (string, error) {
// Get the default branch name
- defaultBranch, err := getRepoDefaultBranch(repo)
+ defaultBranch, err := getRepoDefaultBranch(context.Background(), repo)
if err != nil {
return "", fmt.Errorf("failed to get default branch for %s: %w", repo, err)
}
@@ -225,8 +226,8 @@ func resolveLatestCommitFromDefaultBranch(repo, currentSHA string, verbose bool)
}
// getRepoDefaultBranch fetches the default branch name for a repository.
-func getRepoDefaultBranch(repo string) (string, error) {
- output, err := workflow.RunGH("Fetching repo info...", "api", "/repos/"+repo, "--jq", ".default_branch")
+func getRepoDefaultBranch(ctx context.Context, repo string) (string, error) {
+ output, err := workflow.RunGHContext(ctx, "Fetching repo info...", "api", "/repos/"+repo, "--jq", ".default_branch")
if err != nil {
return "", err
}
diff --git a/pkg/cli/workflows.go b/pkg/cli/workflows.go
index 3bd320b518a..4fc716008d5 100644
--- a/pkg/cli/workflows.go
+++ b/pkg/cli/workflows.go
@@ -350,6 +350,22 @@ func extractWorkflowNameFromFile(filePath string) (string, error) {
return strings.Join(words, " "), nil
}
+// extractEngineIDFromFrontmatter extracts the engine ID from a parsed frontmatter map.
+// Returns "copilot" as the default if no engine is specified.
+func extractEngineIDFromFrontmatter(frontmatter map[string]any) string {
+ // Use the workflow package's ExtractEngineConfig to handle both string and object formats
+ compiler := &workflow.Compiler{}
+ engineSetting, engineConfig := compiler.ExtractEngineConfig(frontmatter)
+
+ if engineConfig != nil && engineConfig.ID != "" {
+ return engineConfig.ID
+ }
+ if engineSetting != "" {
+ return engineSetting
+ }
+ return "copilot" // Default engine
+}
+
// extractEngineIDFromFile extracts the engine ID from a workflow file's frontmatter
func extractEngineIDFromFile(filePath string) string {
content, err := os.ReadFile(filePath)
@@ -363,21 +379,7 @@ func extractEngineIDFromFile(filePath string) string {
return "" // Return empty string if frontmatter cannot be parsed
}
- // Use the workflow package's extractEngineConfig to handle both string and object formats
- compiler := &workflow.Compiler{}
- engineSetting, engineConfig := compiler.ExtractEngineConfig(result.Frontmatter)
-
- // If engine is specified, return the ID from the config
- if engineConfig != nil && engineConfig.ID != "" {
- return engineConfig.ID
- }
-
- // If we have an engine setting string, return it
- if engineSetting != "" {
- return engineSetting
- }
-
- return "copilot" // Default engine
+ return extractEngineIDFromFrontmatter(result.Frontmatter)
}
// normalizeWorkflowID extracts the workflow ID from a workflow identifier.
diff --git a/pkg/console/console.go b/pkg/console/console.go
index 606dfbc4be4..7834373bd54 100644
--- a/pkg/console/console.go
+++ b/pkg/console/console.go
@@ -3,6 +3,7 @@
package console
import (
+ "errors"
"fmt"
"os"
"strconv"
@@ -245,6 +246,86 @@ func FormatErrorMessage(message string) string {
return applyStyle(styles.Error, "✗ ") + message
}
+// FormatErrorChain formats an error and its full unwrapped chain in a reading-friendly way.
+// For wrapped errors (fmt.Errorf with %w), each level of the chain is shown on a new
+// indented line. For errors whose message contains newlines (e.g. errors.Join), each
+// line is indented after the first.
+func FormatErrorChain(err error) string {
+ if err == nil {
+ return ""
+ }
+
+ chain := unwrapErrorChain(err)
+ if len(chain) <= 1 {
+ return formatMultilineError(err.Error())
+ }
+
+ var sb strings.Builder
+ sb.WriteString(applyStyle(styles.Error, "✗ "))
+ sb.WriteString(chain[0])
+ for _, msg := range chain[1:] {
+ // Each message in the chain may itself contain newlines (e.g. from errors.Join
+ // nested inside a wrapping error); expand them all with consistent indentation.
+ for line := range strings.SplitSeq(msg, "\n") {
+ if line != "" {
+ sb.WriteString("\n ")
+ sb.WriteString(line)
+ }
+ }
+ }
+ return sb.String()
+}
+
+// unwrapErrorChain walks the error chain via errors.Unwrap and returns a slice of
+// individual message contributions, from outermost to innermost. Each entry contains
+// only the message added at that level (i.e. the inner error's message is stripped).
+func unwrapErrorChain(err error) []string {
+ var chain []string
+ current := err
+ for current != nil {
+ next := errors.Unwrap(current)
+ if next == nil {
+ chain = append(chain, current.Error())
+ break
+ }
+ outerMsg := current.Error()
+ innerMsg := next.Error()
+ // Strip the inner error's message from the current error's message
+ // to isolate this level's own contribution. This assumes the standard
+ // fmt.Errorf("prefix: %w", inner) pattern (colon-space separator).
+ // If the pattern does not match, the full message is used as a fallback
+ // so no information is lost.
+ suffix := ": " + innerMsg
+ if strings.HasSuffix(outerMsg, suffix) {
+ chain = append(chain, outerMsg[:len(outerMsg)-len(suffix)])
+ } else {
+ // Format does not follow the standard ": %w" pattern; keep the full message.
+ chain = append(chain, outerMsg)
+ }
+ current = next
+ }
+ return chain
+}
+
+// formatMultilineError formats a plain error message, indenting any newlines so that
+// continuation lines are visually subordinate to the leading "✗" prefix.
+func formatMultilineError(msg string) string {
+ if !strings.Contains(msg, "\n") {
+ return FormatErrorMessage(msg)
+ }
+ lines := strings.Split(msg, "\n")
+ var sb strings.Builder
+ sb.WriteString(applyStyle(styles.Error, "✗ "))
+ sb.WriteString(lines[0])
+ for _, line := range lines[1:] {
+ if line != "" {
+ sb.WriteString("\n ")
+ sb.WriteString(line)
+ }
+ }
+ return sb.String()
+}
+
// FormatSectionHeader formats a section header with proper styling
func FormatSectionHeader(header string) string {
if isTTY() {
diff --git a/pkg/console/console_formatting_test.go b/pkg/console/console_formatting_test.go
index f96cd19e067..86fa02f6ef8 100644
--- a/pkg/console/console_formatting_test.go
+++ b/pkg/console/console_formatting_test.go
@@ -3,6 +3,8 @@
package console
import (
+ "errors"
+ "fmt"
"strings"
"testing"
)
@@ -375,3 +377,104 @@ func TestFormattingFunctionsWithUnicodeCharacters(t *testing.T) {
}
})
}
+
+func TestFormatErrorChain(t *testing.T) {
+ tests := []struct {
+ name string
+ err error
+ expectedContains []string
+ expectMultiLine bool
+ }{
+ {
+ name: "nil error",
+ err: nil,
+ expectedContains: []string{},
+ expectMultiLine: false,
+ },
+ {
+ name: "simple single error",
+ err: errors.New("file not found"),
+ expectedContains: []string{"✗", "file not found"},
+ expectMultiLine: false,
+ },
+ {
+ name: "two-level wrapped error",
+ err: fmt.Errorf("outer: %w", errors.New("inner cause")),
+ expectedContains: []string{"✗", "outer", "inner cause"},
+ expectMultiLine: true,
+ },
+ {
+ name: "three-level wrapped error chain",
+ err: fmt.Errorf("workflow not found: %w",
+ fmt.Errorf("failed to download: %w",
+ errors.New("HTTP 404: Not Found"))),
+ expectedContains: []string{"✗", "workflow not found", "failed to download", "HTTP 404: Not Found"},
+ expectMultiLine: true,
+ },
+ {
+ name: "multiline error from errors.Join",
+ err: errors.Join(errors.New("error one"), errors.New("error two")),
+ expectedContains: []string{"✗", "error one", "error two"},
+ expectMultiLine: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := FormatErrorChain(tt.err)
+
+ for _, expected := range tt.expectedContains {
+ if !strings.Contains(result, expected) {
+ t.Errorf("FormatErrorChain() = %q, should contain %q", result, expected)
+ }
+ }
+
+ if tt.expectMultiLine && !strings.Contains(result, "\n") {
+ t.Errorf("FormatErrorChain() should produce multi-line output for wrapped/joined errors, got: %q", result)
+ }
+
+ // Verify indentation: continuation lines should start with spaces, first line should not
+ if tt.expectMultiLine && tt.err != nil {
+ lines := strings.Split(result, "\n")
+ if len(lines) > 1 {
+ // First line must start with the ✗ symbol (not spaces)
+ if strings.HasPrefix(lines[0], " ") {
+ t.Errorf("FormatErrorChain() first line should not be indented, got: %q", lines[0])
+ }
+ if !strings.Contains(lines[0], "✗") {
+ t.Errorf("FormatErrorChain() first line should contain ✗ symbol, got: %q", lines[0])
+ }
+ // Continuation lines must be indented
+ for _, line := range lines[1:] {
+ if line != "" && !strings.HasPrefix(line, " ") {
+ t.Errorf("FormatErrorChain() continuation line should be indented with 2 spaces, got: %q", line)
+ }
+ }
+ }
+ }
+ })
+ }
+}
+
+// TestFormatErrorChainDoesNotRepeatContext verifies that the chain format does not
+// duplicate inner messages on the first line when errors are properly wrapped.
+func TestFormatErrorChainDoesNotRepeatContext(t *testing.T) {
+ inner := errors.New("HTTP 404: Not Found")
+ middle := fmt.Errorf("failed to fetch file: %w", inner)
+ outer := fmt.Errorf("workflow not found: %w", middle)
+
+ result := FormatErrorChain(outer)
+ lines := strings.Split(result, "\n")
+
+ if len(lines) < 3 {
+ t.Fatalf("expected at least 3 lines, got %d: %q", len(lines), result)
+ }
+
+ // First line should only contain the outermost message prefix, not inner messages
+ if strings.Contains(lines[0], "failed to fetch file") {
+ t.Errorf("first line should not contain middle message, got: %q", lines[0])
+ }
+ if strings.Contains(lines[0], "HTTP 404") {
+ t.Errorf("first line should not contain innermost message, got: %q", lines[0])
+ }
+}
diff --git a/pkg/console/render.go b/pkg/console/render.go
index 5faa8baeaab..9f92849feb2 100644
--- a/pkg/console/render.go
+++ b/pkg/console/render.go
@@ -154,6 +154,8 @@ func renderSlice(val reflect.Value, title string, output *strings.Builder, depth
return
}
+ renderLog.Printf("Rendering slice: title=%s, length=%d, element_type=%s", title, val.Len(), val.Type().Elem().Name())
+
// Print title without FormatInfoMessage styling
if title != "" {
if depth == 0 {
@@ -208,6 +210,8 @@ func renderMap(val reflect.Value, title string, output *strings.Builder, depth i
// buildTableConfig builds a TableConfig from a slice of structs
func buildTableConfig(val reflect.Value, title string) TableConfig {
+ renderLog.Printf("Building table config: title=%s, elements=%d", title, val.Len())
+
config := TableConfig{
Title: "",
}
diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go
index 21d35a520eb..fdaf0147cf5 100644
--- a/pkg/constants/constants.go
+++ b/pkg/constants/constants.go
@@ -343,8 +343,13 @@ const DefaultGeminiVersion Version = "latest"
// DefaultGitHubMCPServerVersion is the default version of the GitHub MCP server Docker image
const DefaultGitHubMCPServerVersion Version = "v0.32.0"
+// DefaultGitHubLockdown is the default value for the GitHub MCP server lockdown setting.
+// Lockdown mode restricts the GitHub MCP server to the triggering repository only.
+// Defaults to false (lockdown disabled).
+const DefaultGitHubLockdown = false
+
// DefaultFirewallVersion is the default version of the gh-aw-firewall (AWF) binary
-const DefaultFirewallVersion Version = "v0.24.1"
+const DefaultFirewallVersion Version = "v0.24.2"
// AWF (Agentic Workflow Firewall) constants
@@ -401,6 +406,9 @@ var SerenaLanguageSupport = map[string][]string{
},
}
+// DefaultAPMVersion is the default version of the microsoft/APM (Agent Package Manager) CLI
+const DefaultAPMVersion Version = "v0.8.0"
+
// DefaultPlaywrightMCPVersion is the default version of the @playwright/mcp package
const DefaultPlaywrightMCPVersion Version = "0.0.68"
@@ -435,9 +443,13 @@ const DefaultAlpineImage = "alpine:latest"
// This image is built during workflow execution and includes the gh-aw binary and dependencies
const DevModeGhAwImage = "localhost/gh-aw:dev"
+// GhAwHomeDefault is the default value for GH_AW_HOME when the env var is not set
+const GhAwHomeDefault = "/opt/gh-aw"
+
// DefaultGhAwMount is the mount path for the gh-aw directory in containerized MCP servers
-// The gh-aw binary and supporting files are mounted read-only from /opt/gh-aw
-const DefaultGhAwMount = "/opt/gh-aw:/opt/gh-aw:ro"
+// Uses shell expansion so docker gets the resolved path at runtime.
+// GH_AW_HOME is always set in the job-level env, so no fallback is needed here.
+const DefaultGhAwMount = "\\${GH_AW_HOME}:\\${GH_AW_HOME}:ro"
// DefaultGhBinaryMount is the mount path for the gh CLI binary in containerized MCP servers
// The gh CLI is required for agentic-workflows MCP server to run gh commands
@@ -672,6 +684,7 @@ const CheckStopTimeStepID StepID = "check_stop_time"
const CheckSkipIfMatchStepID StepID = "check_skip_if_match"
const CheckSkipIfNoMatchStepID StepID = "check_skip_if_no_match"
const CheckCommandPositionStepID StepID = "check_command_position"
+const RemoveTriggerLabelStepID StepID = "remove_trigger_label"
const CheckRateLimitStepID StepID = "check_rate_limit"
const CheckSkipRolesStepID StepID = "check_skip_roles"
const CheckSkipBotsStepID StepID = "check_skip_bots"
diff --git a/pkg/fileutil/fileutil_test.go b/pkg/fileutil/fileutil_test.go
index 2019f4596ca..95c73f2e3d7 100644
--- a/pkg/fileutil/fileutil_test.go
+++ b/pkg/fileutil/fileutil_test.go
@@ -3,6 +3,9 @@
package fileutil
import (
+ "archive/tar"
+ "bytes"
+ "os"
"path/filepath"
"runtime"
"strings"
@@ -176,3 +179,190 @@ func TestValidateAbsolutePath_SecurityScenarios(t *testing.T) {
})
}
}
+
+func TestFileExists(t *testing.T) {
+ dir := t.TempDir()
+
+ // Create a real file to test against
+ filePath := filepath.Join(dir, "test.txt")
+ require.NoError(t, os.WriteFile(filePath, []byte("hello"), 0600), "Should create temp file")
+
+ tests := []struct {
+ name string
+ path string
+ expected bool
+ }{
+ {
+ name: "existing file returns true",
+ path: filePath,
+ expected: true,
+ },
+ {
+ name: "non-existent path returns false",
+ path: filepath.Join(dir, "does_not_exist.txt"),
+ expected: false,
+ },
+ {
+ name: "directory path returns false",
+ path: dir,
+ expected: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := FileExists(tt.path)
+ assert.Equal(t, tt.expected, result, "FileExists(%q) should return %v", tt.path, tt.expected)
+ })
+ }
+}
+
+func TestDirExists(t *testing.T) {
+ dir := t.TempDir()
+
+ // Create a real file to use as a non-directory path
+ filePath := filepath.Join(dir, "test.txt")
+ require.NoError(t, os.WriteFile(filePath, []byte("hello"), 0600), "Should create temp file")
+
+ tests := []struct {
+ name string
+ path string
+ expected bool
+ }{
+ {
+ name: "existing directory returns true",
+ path: dir,
+ expected: true,
+ },
+ {
+ name: "non-existent path returns false",
+ path: filepath.Join(dir, "does_not_exist"),
+ expected: false,
+ },
+ {
+ name: "file path returns false",
+ path: filePath,
+ expected: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := DirExists(tt.path)
+ assert.Equal(t, tt.expected, result, "DirExists(%q) should return %v", tt.path, tt.expected)
+ })
+ }
+}
+
+func TestIsDirEmpty(t *testing.T) {
+ t.Run("empty directory returns true", func(t *testing.T) {
+ dir := t.TempDir()
+ assert.True(t, IsDirEmpty(dir), "Newly created temp dir should be empty")
+ })
+
+ t.Run("non-empty directory returns false", func(t *testing.T) {
+ dir := t.TempDir()
+ require.NoError(t, os.WriteFile(filepath.Join(dir, "file.txt"), []byte("data"), 0600), "Should create file in dir")
+ assert.False(t, IsDirEmpty(dir), "Dir with a file should not be empty")
+ })
+
+ t.Run("unreadable directory returns true", func(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("Permission-based test not applicable on Windows")
+ }
+ dir := t.TempDir()
+ unreadable := filepath.Join(dir, "unreadable")
+ require.NoError(t, os.Mkdir(unreadable, 0000), "Should create unreadable dir")
+ t.Cleanup(func() { _ = os.Chmod(unreadable, 0700) })
+ assert.True(t, IsDirEmpty(unreadable), "Unreadable directory should be treated as empty")
+ })
+}
+
+func TestCopyFile(t *testing.T) {
+ t.Run("successful copy", func(t *testing.T) {
+ dir := t.TempDir()
+ src := filepath.Join(dir, "src.txt")
+ dst := filepath.Join(dir, "dst.txt")
+ content := []byte("file content")
+
+ require.NoError(t, os.WriteFile(src, content, 0600), "Should create source file")
+
+ err := CopyFile(src, dst)
+ require.NoError(t, err, "CopyFile should succeed for valid src and dst")
+
+ got, readErr := os.ReadFile(dst)
+ require.NoError(t, readErr, "Should be able to read copied file")
+ assert.Equal(t, content, got, "Copied file content should match source")
+ })
+
+ t.Run("missing source file returns error", func(t *testing.T) {
+ dir := t.TempDir()
+ src := filepath.Join(dir, "nonexistent.txt")
+ dst := filepath.Join(dir, "dst.txt")
+
+ err := CopyFile(src, dst)
+ require.Error(t, err, "CopyFile should return error when source does not exist")
+ })
+
+ t.Run("missing destination directory returns error", func(t *testing.T) {
+ dir := t.TempDir()
+ src := filepath.Join(dir, "src.txt")
+ dst := filepath.Join(dir, "missing_dir", "dst.txt")
+
+ require.NoError(t, os.WriteFile(src, []byte("data"), 0600), "Should create source file")
+
+ err := CopyFile(src, dst)
+ require.Error(t, err, "CopyFile should return error when destination directory does not exist")
+ })
+}
+
+func TestExtractFileFromTar(t *testing.T) {
+ // Helper to build an in-memory tar archive
+ buildTar := func(files map[string][]byte) []byte {
+ var buf bytes.Buffer
+ tw := tar.NewWriter(&buf)
+ for name, content := range files {
+ hdr := &tar.Header{
+ Name: name,
+ Mode: 0600,
+ Size: int64(len(content)),
+ }
+ if err := tw.WriteHeader(hdr); err != nil {
+ t.Fatalf("buildTar: WriteHeader: %v", err)
+ }
+ if _, err := tw.Write(content); err != nil {
+ t.Fatalf("buildTar: Write: %v", err)
+ }
+ }
+ if err := tw.Close(); err != nil {
+ t.Fatalf("buildTar: Close: %v", err)
+ }
+ return buf.Bytes()
+ }
+
+ t.Run("found file returns its content", func(t *testing.T) {
+ want := []byte("hello from tar")
+ archive := buildTar(map[string][]byte{"subdir/file.txt": want})
+
+ got, err := ExtractFileFromTar(archive, "subdir/file.txt")
+ require.NoError(t, err, "ExtractFileFromTar should succeed when file is present")
+ assert.Equal(t, want, got, "Extracted content should match original")
+ })
+
+ t.Run("file not found returns error", func(t *testing.T) {
+ archive := buildTar(map[string][]byte{"other.txt": []byte("data")})
+
+ got, err := ExtractFileFromTar(archive, "missing.txt")
+ require.Error(t, err, "ExtractFileFromTar should return error when file is absent")
+ assert.Contains(t, err.Error(), "missing.txt", "Error should mention the missing filename")
+ assert.Nil(t, got, "Result should be nil when file is not found")
+ })
+
+ t.Run("corrupted archive returns error", func(t *testing.T) {
+ corrupted := []byte("this is not a valid tar archive")
+
+ got, err := ExtractFileFromTar(corrupted, "any.txt")
+ require.Error(t, err, "ExtractFileFromTar should return error for corrupted archive")
+ assert.Nil(t, got, "Result should be nil for corrupted archive")
+ })
+}
diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go
index a0303644102..f7e169e63f5 100644
--- a/pkg/logger/logger.go
+++ b/pkg/logger/logger.go
@@ -23,7 +23,8 @@ type Logger struct {
var (
// DEBUG environment variable value, read once at initialization.
- debugEnv = os.Getenv("DEBUG")
+ // If DEBUG is not set but ACTIONS_RUNNER_DEBUG=true, all loggers are enabled.
+ debugEnv = initDebugEnv()
// DEBUG_COLORS environment variable to control color output.
debugColors = os.Getenv("DEBUG_COLORS") != "0"
@@ -51,6 +52,19 @@ var (
colorReset = "\033[0m"
)
+// initDebugEnv resolves the effective debug pattern.
+// If DEBUG is set, it takes precedence. Otherwise, if ACTIONS_RUNNER_DEBUG=true,
+// all loggers are enabled (equivalent to DEBUG=*).
+func initDebugEnv() string {
+ if d := os.Getenv("DEBUG"); d != "" {
+ return d
+ }
+ if os.Getenv("ACTIONS_RUNNER_DEBUG") == "true" {
+ return "*"
+ }
+ return ""
+}
+
// New creates a new Logger for the given namespace.
// The enabled state is computed at construction time based on the DEBUG environment variable.
// DEBUG syntax follows https://www.npmjs.com/package/debug patterns:
diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go
index ebf55df4852..5de982fa99e 100644
--- a/pkg/logger/logger_test.go
+++ b/pkg/logger/logger_test.go
@@ -332,6 +332,52 @@ func TestColorDisabling(t *testing.T) {
}
}
+func TestInitDebugEnv(t *testing.T) {
+ tests := []struct {
+ name string
+ debugEnvVal string
+ actionsRunnerDebug string
+ want string
+ }{
+ {
+ name: "DEBUG takes precedence over ACTIONS_RUNNER_DEBUG",
+ debugEnvVal: "cli:*",
+ actionsRunnerDebug: "true",
+ want: "cli:*",
+ },
+ {
+ name: "ACTIONS_RUNNER_DEBUG=true enables all loggers",
+ debugEnvVal: "",
+ actionsRunnerDebug: "true",
+ want: "*",
+ },
+ {
+ name: "ACTIONS_RUNNER_DEBUG=false does not enable loggers",
+ debugEnvVal: "",
+ actionsRunnerDebug: "false",
+ want: "",
+ },
+ {
+ name: "neither set returns empty",
+ debugEnvVal: "",
+ actionsRunnerDebug: "",
+ want: "",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Setenv("DEBUG", tt.debugEnvVal)
+ t.Setenv("ACTIONS_RUNNER_DEBUG", tt.actionsRunnerDebug)
+
+ got := initDebugEnv()
+ if got != tt.want {
+ t.Errorf("initDebugEnv() = %q, want %q", got, tt.want)
+ }
+ })
+ }
+}
+
func TestMatchPattern(t *testing.T) {
tests := []struct {
name string
diff --git a/pkg/parser/frontmatter_helpers_test.go b/pkg/parser/frontmatter_helpers_test.go
index c32905708f2..5ad75521d08 100644
--- a/pkg/parser/frontmatter_helpers_test.go
+++ b/pkg/parser/frontmatter_helpers_test.go
@@ -6,10 +6,11 @@ import (
"errors"
"os"
"path/filepath"
- "strings"
"testing"
"github.com/github/gh-aw/pkg/testutil"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestUpdateWorkflowFrontmatter(t *testing.T) {
@@ -17,11 +18,11 @@ func TestUpdateWorkflowFrontmatter(t *testing.T) {
tempDir := testutil.TempDir(t, "test-*")
tests := []struct {
- name string
- initialContent string
- updateFunc func(frontmatter map[string]any) error
- expectedContent string
- expectError bool
+ name string
+ initialContent string
+ updateFunc func(frontmatter map[string]any) error
+ expectError bool
+ verbose bool
}{
{
name: "Add tool to existing tools section",
@@ -36,17 +37,6 @@ Some content`,
tools["new-tool"] = map[string]any{"type": "test"}
return nil
},
- expectedContent: `---
-existing: {}
-new-tool:
- type: test
-tools:
- existing: {}
- new-tool:
- type: test
----
-# Test Workflow
-Some content`,
expectError: false,
},
{
@@ -61,14 +51,6 @@ Some content`,
tools["new-tool"] = map[string]any{"type": "test"}
return nil
},
- expectedContent: `---
-engine: claude
-tools:
- new-tool:
- type: test
----
-# Test Workflow
-Some content`,
expectError: false,
},
{
@@ -82,13 +64,6 @@ Some content`,
tools["new-tool"] = map[string]any{"type": "test"}
return nil
},
- expectedContent: `---
-tools:
- new-tool:
- type: test
----
-# Test Workflow
-Some content`,
expectError: false,
},
{
@@ -100,13 +75,6 @@ Some content without frontmatter`,
tools["new-tool"] = map[string]any{"type": "test"}
return nil
},
- expectedContent: `---
-tools:
- new-tool:
- type: test
----
-# Test Workflow
-Some content without frontmatter`,
expectError: false,
},
{
@@ -118,8 +86,20 @@ tools: {}
updateFunc: func(frontmatter map[string]any) error {
return errors.New("test error")
},
- expectedContent: "",
- expectError: true,
+ expectError: true,
+ },
+ {
+ name: "Verbose mode emits info message to stderr",
+ initialContent: `---
+engine: claude
+---
+# Test`,
+ updateFunc: func(frontmatter map[string]any) error {
+ frontmatter["engine"] = "copilot"
+ return nil
+ },
+ expectError: false,
+ verbose: true,
},
}
@@ -127,44 +107,41 @@ tools: {}
t.Run(tt.name, func(t *testing.T) {
// Create test file
testFile := filepath.Join(tempDir, "test-workflow.md")
- if err := os.WriteFile(testFile, []byte(tt.initialContent), 0644); err != nil {
- t.Fatalf("Failed to create test file: %v", err)
+ err := os.WriteFile(testFile, []byte(tt.initialContent), 0644)
+ require.NoError(t, err, "test file setup should succeed")
+
+ // Run the update function, capturing stderr to detect verbose output
+ var stderr string
+ if tt.verbose {
+ stderr = testutil.CaptureStderr(t, func() {
+ err = UpdateWorkflowFrontmatter(testFile, tt.updateFunc, true)
+ })
+ } else {
+ err = UpdateWorkflowFrontmatter(testFile, tt.updateFunc, false)
}
- // Run the update function
- err := UpdateWorkflowFrontmatter(testFile, tt.updateFunc, false)
-
if tt.expectError {
- if err == nil {
- t.Errorf("Expected error but got none")
- }
+ assert.Error(t, err, "UpdateWorkflowFrontmatter should return an error")
return
}
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- return
- }
+ require.NoError(t, err, "UpdateWorkflowFrontmatter should succeed")
// Read the updated content
updatedContent, err := os.ReadFile(testFile)
- if err != nil {
- t.Fatalf("Failed to read updated file: %v", err)
- }
-
- // For tools section tests, just verify the tools were added correctly
- // Skip exact content comparison since YAML marshaling order may vary
- if strings.Contains(tt.name, "tool") {
- content := string(updatedContent)
- if !strings.Contains(content, "new-tool:") {
- t.Errorf("Expected 'new-tool:' in updated content, got: %s", content)
- }
- if !strings.Contains(content, "type: test") {
- t.Errorf("Expected 'type: test' in updated content, got: %s", content)
- }
- if !strings.Contains(content, "---") {
- t.Errorf("Expected frontmatter delimiters '---' in updated content, got: %s", content)
- }
+ require.NoError(t, err, "reading updated file should succeed")
+
+ content := string(updatedContent)
+ if tt.verbose {
+ // Verbose mode: verify the info message was emitted to stderr
+ assert.Contains(t, stderr, "Updated workflow file", "verbose mode should emit an info message to stderr")
+ // Verify the update was still applied correctly
+ assert.Contains(t, content, "engine: copilot", "verbose mode should still update frontmatter correctly")
+ assert.Contains(t, content, "---", "verbose mode should preserve frontmatter delimiters")
+ } else {
+ assert.Contains(t, content, "new-tool:", "updated file should contain the new tool key")
+ assert.Contains(t, content, "type: test", "updated file should contain the tool type")
+ assert.Contains(t, content, "---", "updated file should preserve frontmatter delimiters")
}
})
}
@@ -205,41 +182,19 @@ func TestEnsureToolsSection(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
tools := EnsureToolsSection(tt.frontmatter)
- // Verify tools section is a map
- if tools == nil {
- t.Errorf("Expected tools to be a map, got nil")
- return
- }
+ require.NotNil(t, tools, "EnsureToolsSection should never return nil")
- // Verify frontmatter was updated
+ // Verify frontmatter was updated to contain the tools map
frontmatterTools, ok := tt.frontmatter["tools"].(map[string]any)
- if !ok {
- t.Errorf("Expected frontmatter['tools'] to be a map")
- return
- }
+ require.True(t, ok, "frontmatter['tools'] should be a map[string]any")
- // Verify returned tools is the same reference as in frontmatter
- if len(tools) != len(frontmatterTools) {
- t.Errorf("Expected returned tools to have same length as frontmatter['tools']")
- }
+ // Verify reference identity: mutating the returned map must be visible via frontmatter
+ tools["__probe__"] = true
+ assert.Equal(t, true, frontmatterTools["__probe__"], "returned tools should be the same map stored in frontmatter['tools']")
+ delete(tools, "__probe__")
- // For existing tools, verify content
- if len(tt.expectedTools) > 0 {
- for key, expectedValue := range tt.expectedTools {
- if actualValue, exists := tools[key]; !exists {
- t.Errorf("Expected tool '%s' not found", key)
- } else {
- // Simple comparison for test values
- if expectedMap, ok := expectedValue.(map[string]any); ok {
- if actualMap, ok := actualValue.(map[string]any); ok {
- if expectedMap["type"] != actualMap["type"] {
- t.Errorf("Expected tool '%s' type '%v', got '%v'", key, expectedMap["type"], actualMap["type"])
- }
- }
- }
- }
- }
- }
+ // Verify returned tools matches the expected content
+ assert.Equal(t, tt.expectedTools, tools, "tools section should match expected state")
})
}
}
@@ -280,14 +235,8 @@ func TestReconstructWorkflowFile(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := reconstructWorkflowFile(tt.frontmatterYAML, tt.markdownContent)
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- return
- }
-
- if result != tt.expectedResult {
- t.Errorf("Expected:\n%s\n\nGot:\n%s", tt.expectedResult, result)
- }
+ require.NoError(t, err, "reconstructWorkflowFile should succeed")
+ assert.Equal(t, tt.expectedResult, result, "reconstructed file content should match")
})
}
}
diff --git a/pkg/parser/import_directive.go b/pkg/parser/import_directive.go
index 89a0b06c980..c365157e607 100644
--- a/pkg/parser/import_directive.go
+++ b/pkg/parser/import_directive.go
@@ -28,14 +28,20 @@ type ImportDirectiveMatch struct {
func ParseImportDirective(line string) *ImportDirectiveMatch {
trimmedLine := strings.TrimSpace(line)
+ // Fast-path: import directives must start with '@' or '{'; skip the regex for all other lines.
+ if len(trimmedLine) == 0 || (trimmedLine[0] != '@' && trimmedLine[0] != '{') {
+ return nil
+ }
+
// Check if it matches the import pattern at all
matches := IncludeDirectivePattern.FindStringSubmatch(trimmedLine)
if matches == nil {
return nil
}
- // Check if it's legacy syntax
- isLegacy := LegacyIncludeDirectivePattern.MatchString(trimmedLine)
+ // Determine legacy vs new syntax from the captured groups of the first match.
+ // Group 2 (path for @include/@import) is non-empty iff the legacy alternative matched.
+ isLegacy := matches[2] != ""
importDirectiveLog.Printf("Parsing import directive: legacy=%t, line=%s", isLegacy, trimmedLine)
var isOptional bool
diff --git a/pkg/parser/import_remote_nested_test.go b/pkg/parser/import_remote_nested_test.go
index 9c8c716b6e9..034ba81d75a 100644
--- a/pkg/parser/import_remote_nested_test.go
+++ b/pkg/parser/import_remote_nested_test.go
@@ -470,7 +470,7 @@ func TestResolveRemoteSymlinksPathConstruction(t *testing.T) {
// GitHub API access, which is tested in integration tests.
t.Run("single component path returns error", func(t *testing.T) {
- _, err := resolveRemoteSymlinks("owner", "repo", "file.md", "main")
+ _, err := resolveRemoteSymlinks(nil, "owner", "repo", "file.md", "main")
assert.Error(t, err, "Single component path has no directories to resolve")
})
diff --git a/pkg/parser/remote_fetch.go b/pkg/parser/remote_fetch.go
index 7388e7f3d98..8ba11575062 100644
--- a/pkg/parser/remote_fetch.go
+++ b/pkg/parser/remote_fetch.go
@@ -249,7 +249,7 @@ func downloadIncludeFromWorkflowSpec(spec string, cache *ImportCache) (string, e
var sha string
if cache != nil {
// Only resolve SHA if we're using the cache
- resolvedSHA, err := resolveRefToSHA(owner, repo, ref)
+ resolvedSHA, err := resolveRefToSHA(owner, repo, ref, "")
if err != nil {
// SHA resolution failure (including auth errors) only means we cannot cache; the
// actual file download will be attempted below and may succeed via git fallback for
@@ -316,10 +316,15 @@ func downloadIncludeFromWorkflowSpec(spec string, cache *ImportCache) (string, e
// resolveRefToSHAViaGit resolves a git ref to SHA using git ls-remote
// This is a fallback for when GitHub API authentication fails
-func resolveRefToSHAViaGit(owner, repo, ref string) (string, error) {
+func resolveRefToSHAViaGit(owner, repo, ref, host string) (string, error) {
remoteLog.Printf("Attempting git ls-remote fallback for ref resolution: %s/%s@%s", owner, repo, ref)
- githubHost := GetGitHubHostForRepo(owner, repo)
+ var githubHost string
+ if host != "" {
+ githubHost = "https://" + host
+ } else {
+ githubHost = GetGitHubHostForRepo(owner, repo)
+ }
repoURL := fmt.Sprintf("%s/%s/%s.git", githubHost, owner, repo)
// Try to resolve the ref using git ls-remote
@@ -365,7 +370,7 @@ func resolveRefToSHAViaGit(owner, repo, ref string) (string, error) {
}
// resolveRefToSHA resolves a git ref (branch, tag, or SHA) to its commit SHA
-func resolveRefToSHA(owner, repo, ref string) (string, error) {
+func resolveRefToSHA(owner, repo, ref, host string) (string, error) {
// If ref is already a full SHA (40 hex characters), return it as-is
if len(ref) == 40 && gitutil.IsHexString(ref) {
return ref, nil
@@ -374,14 +379,21 @@ func resolveRefToSHA(owner, repo, ref string) (string, error) {
// Use gh CLI to get the commit SHA for the ref
// This works for branches, tags, and short SHAs
// Using go-gh to properly handle enterprise GitHub instances via GH_HOST
- stdout, stderr, err := gh.Exec("api", fmt.Sprintf("/repos/%s/%s/commits/%s", owner, repo, ref), "--jq", ".sha")
+ apiPath := fmt.Sprintf("/repos/%s/%s/commits/%s", owner, repo, ref)
+ var args []string
+ if host != "" {
+ args = []string{"api", "--hostname", host, apiPath, "--jq", ".sha"}
+ } else {
+ args = []string{"api", apiPath, "--jq", ".sha"}
+ }
+ stdout, stderr, err := gh.Exec(args...)
if err != nil {
outputStr := stderr.String()
if gitutil.IsAuthError(outputStr) {
remoteLog.Printf("GitHub API authentication failed, attempting git ls-remote fallback for %s/%s@%s", owner, repo, ref)
// Try fallback using git ls-remote for public repositories
- sha, gitErr := resolveRefToSHAViaGit(owner, repo, ref)
+ sha, gitErr := resolveRefToSHAViaGit(owner, repo, ref, host)
if gitErr != nil {
// If git fallback also fails, return both errors
return "", fmt.Errorf("failed to resolve ref via GitHub API (auth error) and git ls-remote: API error: %w, Git error: %w", err, gitErr)
@@ -406,20 +418,29 @@ func resolveRefToSHA(owner, repo, ref string) (string, error) {
// downloadFileViaGit downloads a file from a Git repository using git commands
// This is a fallback for when GitHub API authentication fails
-func downloadFileViaGit(owner, repo, path, ref string) ([]byte, error) {
+func downloadFileViaGit(owner, repo, path, ref, host string) ([]byte, error) {
remoteLog.Printf("Attempting git fallback for %s/%s/%s@%s", owner, repo, path, ref)
// First, try via raw.githubusercontent.com — no auth required for public repos and
// no dependency on git being installed.
- content, rawErr := downloadFileViaRawURL(owner, repo, path, ref)
- if rawErr == nil {
- return content, nil
+ // Only attempt raw URL for github.com repos (not GHE) since raw.githubusercontent.com
+ // only serves public GitHub content.
+ if host == "" || host == "github.com" {
+ content, rawErr := downloadFileViaRawURL(owner, repo, path, ref)
+ if rawErr == nil {
+ return content, nil
+ }
+ remoteLog.Printf("Raw URL download failed for %s/%s/%s@%s, trying git archive: %v", owner, repo, path, ref, rawErr)
}
- remoteLog.Printf("Raw URL download failed for %s/%s/%s@%s, trying git archive: %v", owner, repo, path, ref, rawErr)
// Use git archive to get the file content without cloning
// This works for public repositories without authentication
- githubHost := GetGitHubHostForRepo(owner, repo)
+ var githubHost string
+ if host != "" {
+ githubHost = "https://" + host
+ } else {
+ githubHost = GetGitHubHostForRepo(owner, repo)
+ }
repoURL := fmt.Sprintf("%s/%s/%s.git", githubHost, owner, repo)
// git archive command: git archive --remote= [
@@ -429,11 +450,11 @@ func downloadFileViaGit(owner, repo, path, ref string) ([]byte, error) {
archiveOutput, err := cmd.Output()
if err != nil {
// If git archive fails, try with git clone + git show as a fallback
- return downloadFileViaGitClone(owner, repo, path, ref)
+ return downloadFileViaGitClone(owner, repo, path, ref, host)
}
// Extract the file from the tar archive using Go's archive/tar (cross-platform)
- content, err = fileutil.ExtractFileFromTar(archiveOutput, path)
+ content, err := fileutil.ExtractFileFromTar(archiveOutput, path)
if err != nil {
return nil, fmt.Errorf("failed to extract file from git archive: %w", err)
}
@@ -474,7 +495,7 @@ func downloadFileViaRawURL(owner, repo, filePath, ref string) ([]byte, error) {
// downloadFileViaGitClone downloads a file by shallow cloning the repository
// This is used as a fallback when git archive doesn't work
-func downloadFileViaGitClone(owner, repo, path, ref string) ([]byte, error) {
+func downloadFileViaGitClone(owner, repo, path, ref, host string) ([]byte, error) {
remoteLog.Printf("Attempting git clone fallback for %s/%s/%s@%s", owner, repo, path, ref)
// Create a temporary directory for the shallow clone
@@ -484,7 +505,12 @@ func downloadFileViaGitClone(owner, repo, path, ref string) ([]byte, error) {
}
defer os.RemoveAll(tmpDir)
- githubHost := GetGitHubHostForRepo(owner, repo)
+ var githubHost string
+ if host != "" {
+ githubHost = "https://" + host
+ } else {
+ githubHost = GetGitHubHostForRepo(owner, repo)
+ }
repoURL := fmt.Sprintf("%s/%s/%s.git", githubHost, owner, repo)
// Check if ref is a SHA (40 hex characters)
@@ -580,26 +606,19 @@ func checkRemoteSymlink(client *api.RESTClient, owner, repo, dirPath, ref string
// if .github/workflows/shared is a symlink to ../../gh-agent-workflows/shared,
// fetching .github/workflows/shared/elastic-tools.md returns 404.
// This function walks the path components and resolves any symlinks found.
-func resolveRemoteSymlinks(owner, repo, filePath, ref string) (string, error) {
+// The caller must provide a REST client (already authenticated for the correct host).
+func resolveRemoteSymlinks(client *api.RESTClient, owner, repo, filePath, ref string) (string, error) {
parts := strings.Split(filePath, "/")
if len(parts) <= 1 {
return "", fmt.Errorf("no directory components to resolve in path: %s", filePath)
}
- remoteLog.Printf("Attempting symlink resolution for %s/%s/%s@%s (%d path components)", owner, repo, filePath, ref, len(parts))
-
- client, err := api.DefaultRESTClient()
- if err != nil {
- // When auth is unavailable (e.g., running inside an agentic workflow without credentials),
- // symlink resolution cannot proceed. Return a descriptive error so the caller can skip
- // symlink resolution and proceed without it.
- if gitutil.IsAuthError(err.Error()) {
- remoteLog.Printf("REST client creation failed due to auth error, skipping symlink resolution for %s/%s/%s@%s", owner, repo, filePath, ref)
- return "", fmt.Errorf("skipping symlink resolution: no auth available for %s/%s/%s@%s", owner, repo, filePath, ref)
- }
- return "", fmt.Errorf("failed to create REST client: %w", err)
+ if client == nil {
+ return "", fmt.Errorf("no REST client available for symlink resolution of %s/%s/%s@%s", owner, repo, filePath, ref)
}
+ remoteLog.Printf("Attempting symlink resolution for %s/%s/%s@%s (%d path components)", owner, repo, filePath, ref, len(parts))
+
// Check each directory prefix (not including the final filename) to find symlinks
for i := 1; i < len(parts); i++ {
dirPath := strings.Join(parts[:i], "/")
@@ -665,30 +684,55 @@ func resolveRemoteSymlinks(owner, repo, filePath, ref string) (string, error) {
// - ref: Git reference (branch, tag, or commit SHA)
// Returns the file content as bytes or an error if the file cannot be retrieved.
func DownloadFileFromGitHub(owner, repo, path, ref string) ([]byte, error) {
- return downloadFileFromGitHub(owner, repo, path, ref)
+ return downloadFileFromGitHubWithDepth(owner, repo, path, ref, 0, "")
+}
+
+// DownloadFileFromGitHubForHost downloads a file from a GitHub repository using the GitHub API,
+// targeting a specific GitHub host. Use this when the target repository is on a different host
+// than the one configured via GH_HOST (e.g., fetching from github.com while GH_HOST is a GHE instance).
+// host is the hostname without scheme (e.g., "github.com", "myorg.ghe.com").
+// An empty host uses the default configured host (GH_HOST or github.com).
+func DownloadFileFromGitHubForHost(owner, repo, path, ref, host string) ([]byte, error) {
+ return downloadFileFromGitHubWithDepth(owner, repo, path, ref, 0, host)
}
// ResolveRefToSHA resolves a git ref (branch, tag, or short SHA) to its full commit SHA.
// This is the exported wrapper for resolveRefToSHA.
// If the ref is already a 40-character hex SHA, it returns it as-is.
func ResolveRefToSHA(owner, repo, ref string) (string, error) {
- return resolveRefToSHA(owner, repo, ref)
+ return resolveRefToSHA(owner, repo, ref, "")
+}
+
+// ResolveRefToSHAForHost resolves a git ref to its full commit SHA on a specific GitHub host.
+// Use this when the target repository is on a different host than the one configured via GH_HOST.
+// host is the hostname without scheme (e.g., "github.com", "myorg.ghe.com").
+// An empty host uses the default configured host (GH_HOST or github.com).
+func ResolveRefToSHAForHost(owner, repo, ref, host string) (string, error) {
+ return resolveRefToSHA(owner, repo, ref, host)
}
func downloadFileFromGitHub(owner, repo, path, ref string) ([]byte, error) {
- return downloadFileFromGitHubWithDepth(owner, repo, path, ref, 0)
+ return downloadFileFromGitHubWithDepth(owner, repo, path, ref, 0, "")
}
-func downloadFileFromGitHubWithDepth(owner, repo, path, ref string, symlinkDepth int) ([]byte, error) {
- // Create REST client
- client, err := api.DefaultRESTClient()
+func downloadFileFromGitHubWithDepth(owner, repo, path, ref string, symlinkDepth int, host string) ([]byte, error) {
+ // Create a REST client targeting the correct host.
+ // When host is explicitly specified (e.g., "github.com"), use it directly so that
+ // cross-host fetches work correctly even when GH_HOST is set to a different instance.
+ var client *api.RESTClient
+ var err error
+ if host != "" {
+ client, err = api.NewRESTClient(api.ClientOptions{Host: host})
+ } else {
+ client, err = api.DefaultRESTClient()
+ }
if err != nil {
// When the REST client cannot be created due to missing auth (e.g., running inside an
// agentic workflow without gh CLI credentials), fall back to git-based download so that
// public repositories are still accessible without authentication.
if gitutil.IsAuthError(err.Error()) {
remoteLog.Printf("REST client creation failed due to auth error, attempting git fallback for %s/%s/%s@%s: %v", owner, repo, path, ref, err)
- content, gitErr := downloadFileViaGit(owner, repo, path, ref)
+ content, gitErr := downloadFileViaGit(owner, repo, path, ref, host)
if gitErr != nil {
// Both REST (auth error) and git fallback failed. Return the original auth error
// so callers and tests can detect the auth-unavailable condition and skip/handle
@@ -717,7 +761,7 @@ func downloadFileFromGitHubWithDepth(owner, repo, path, ref string, symlinkDepth
if gitutil.IsAuthError(errStr) {
remoteLog.Printf("GitHub API authentication failed, attempting git fallback for %s/%s/%s@%s", owner, repo, path, ref)
// Try fallback using git commands for public repositories
- content, gitErr := downloadFileViaGit(owner, repo, path, ref)
+ content, gitErr := downloadFileViaGit(owner, repo, path, ref, host)
if gitErr != nil {
// If git fallback also fails, return both errors
return nil, fmt.Errorf("failed to fetch file content via GitHub API (auth error) and git fallback: API error: %w, Git error: %w", err, gitErr)
@@ -728,10 +772,10 @@ func downloadFileFromGitHubWithDepth(owner, repo, path, ref string, symlinkDepth
// Check if this is a 404 — the path may traverse a symlink that the API doesn't follow
if isNotFoundError(errStr) && symlinkDepth < constants.MaxSymlinkDepth {
remoteLog.Printf("File not found at %s/%s/%s@%s, checking for symlinks in path (depth: %d)", owner, repo, path, ref, symlinkDepth)
- resolvedPath, resolveErr := resolveRemoteSymlinks(owner, repo, path, ref)
+ resolvedPath, resolveErr := resolveRemoteSymlinks(client, owner, repo, path, ref)
if resolveErr == nil && resolvedPath != path {
remoteLog.Printf("Retrying download with symlink-resolved path: %s -> %s", path, resolvedPath)
- return downloadFileFromGitHubWithDepth(owner, repo, resolvedPath, ref, symlinkDepth+1)
+ return downloadFileFromGitHubWithDepth(owner, repo, resolvedPath, ref, symlinkDepth+1, host)
}
}
diff --git a/pkg/parser/remote_fetch_integration_test.go b/pkg/parser/remote_fetch_integration_test.go
index f8663c06d66..1e8187661fc 100644
--- a/pkg/parser/remote_fetch_integration_test.go
+++ b/pkg/parser/remote_fetch_integration_test.go
@@ -217,7 +217,12 @@ func TestCheckRemoteSymlink(t *testing.T) {
// directory components of a real path and returns "no symlinks found" when none exist.
func TestResolveRemoteSymlinksNoSymlinks(t *testing.T) {
// "Global/Perl.gitignore" is a real path in github/gitignore with no symlinks
- _, err := resolveRemoteSymlinks("github", "gitignore", "Global/Perl.gitignore", "main")
+ client, err := api.DefaultRESTClient()
+ if err != nil {
+ skipOnAuthError(t, err)
+ return
+ }
+ _, err = resolveRemoteSymlinks(client, "github", "gitignore", "Global/Perl.gitignore", "main")
require.Error(t, err, "Expected error when no symlinks found")
skipOnAuthError(t, err)
diff --git a/pkg/parser/safe_outputs_error_location_test.go b/pkg/parser/safe_outputs_error_location_test.go
index b65af607362..ab2200a3ce8 100644
--- a/pkg/parser/safe_outputs_error_location_test.go
+++ b/pkg/parser/safe_outputs_error_location_test.go
@@ -424,11 +424,11 @@ safe-outputs:
}
errorText := err.Error()
+ // The path is shown without a leading '/'; line:col info appears both in the
+ // file:line:col prefix from console.FormatError and in each detail line.
wantSubstrings := []string{
- "/safe-outputs/create-issue",
- "/safe-outputs/create-discussion",
- "line 5, column 5",
- "line 7, column 5",
+ "safe-outputs/create-issue",
+ "safe-outputs/create-discussion",
}
for _, want := range wantSubstrings {
if !strings.Contains(errorText, want) {
@@ -437,7 +437,8 @@ safe-outputs:
}
}
-// TestFormatSchemaFailureDetailEmptyPath verifies that an empty path is normalised to "/".
+// TestFormatSchemaFailureDetailEmptyPath verifies that an empty path returns the message
+// without any path prefix (the root path "/" is stripped from the display).
func TestFormatSchemaFailureDetailEmptyPath(t *testing.T) {
t.Parallel()
@@ -446,13 +447,18 @@ func TestFormatSchemaFailureDetailEmptyPath(t *testing.T) {
Message: "additional property 'x' not allowed",
}
result := formatSchemaFailureDetail(pathInfo, "", "on: daily\n", 1)
- if !strings.HasPrefix(result, "at '/'") {
- t.Errorf("expected result to start with \"at '/'\", got: %s", result)
+ // Empty/root paths return just the message without a path prefix
+ if strings.HasPrefix(result, "at '/'") {
+ t.Errorf("result should not start with \"at '/'\", got: %s", result)
+ }
+ if !strings.Contains(result, "Unknown property") {
+ t.Errorf("expected result to contain error message, got: %s", result)
}
}
// TestFormatSchemaFailureDetailLineColumn verifies that line/column numbers are
-// included in the formatted detail when the path can be located in YAML.
+// included in the formatted detail when the path can be located in YAML, so that
+// secondary failures in multi-failure output retain their location context.
func TestFormatSchemaFailureDetailLineColumn(t *testing.T) {
t.Parallel()
@@ -462,11 +468,13 @@ func TestFormatSchemaFailureDetailLineColumn(t *testing.T) {
Message: "additional property 'invalid-field' not allowed",
}
result := formatSchemaFailureDetail(pathInfo, "", frontmatterContent, 1)
- if !strings.Contains(result, "line ") || !strings.Contains(result, "column ") {
- t.Errorf("expected result to contain line/column info, got: %s", result)
+ // Line/column info should be included in each detail line for secondary failures.
+ if !strings.Contains(result, "line ") || !strings.Contains(result, "col ") {
+ t.Errorf("expected result to contain line/col info, got: %s", result)
}
- if !strings.Contains(result, "/safe-outputs/create-issue") {
- t.Errorf("expected result to contain path '/safe-outputs/create-issue', got: %s", result)
+ // The field path should still appear (without the leading '/').
+ if !strings.Contains(result, "safe-outputs/create-issue") {
+ t.Errorf("expected result to contain path 'safe-outputs/create-issue', got: %s", result)
}
}
diff --git a/pkg/parser/schedule_time_utils.go b/pkg/parser/schedule_time_utils.go
index c07738e7331..179f5df5ed2 100644
--- a/pkg/parser/schedule_time_utils.go
+++ b/pkg/parser/schedule_time_utils.go
@@ -42,6 +42,7 @@ func isAMPMToken(token string) bool {
// normalizeTimeTokens combines time tokens into a normalized string
// Handles time, AM/PM, and timezone tokens
func normalizeTimeTokens(tokens []string) string {
+ scheduleTimeUtilsLog.Printf("Normalizing time tokens: %v", tokens)
if len(tokens) == 0 {
return ""
}
@@ -62,6 +63,7 @@ func normalizeTimeTokens(tokens []string) string {
}
}
+ scheduleTimeUtilsLog.Printf("Normalized time string: %q", timeStr)
return timeStr
}
diff --git a/pkg/parser/schema_compiler.go b/pkg/parser/schema_compiler.go
index 15bddabd125..f2510199eeb 100644
--- a/pkg/parser/schema_compiler.go
+++ b/pkg/parser/schema_compiler.go
@@ -351,6 +351,13 @@ func validateWithSchemaAndLocation(frontmatter map[string]any, schemaJSON, conte
return err
}
+// formatSchemaFailureDetail builds a single line of schema error detail for one JSONPathInfo.
+// It is called once per failing schema constraint in validateWithSchemaAndLocation, which
+// then joins them into a "Multiple schema validation failures:" message. Because
+// CompilerError.Position only captures the *first* failure's location, each detail line
+// independently includes its own (line N, col M) so secondary failures remain navigable.
+// The old "at '/path' (line N, column M):" prefix is replaced with "'path' (line N, col M):"
+// to remove the schema-jargon "at" keyword and the leading slash.
func formatSchemaFailureDetail(pathInfo JSONPathInfo, schemaJSON, frontmatterContent string, frontmatterStart int) string {
path := pathInfo.Path
if path == "" {
@@ -367,7 +374,7 @@ func formatSchemaFailureDetail(pathInfo JSONPathInfo, schemaJSON, frontmatterCon
message := rewriteAdditionalPropertiesError(cleanOneOfMessage(pathInfo.Message))
// Strip any "at '/path': " prefix from the message to avoid duplication with the
- // "at 'path' (line N, column M):" prefix we prepend below.
+ // "'path' (line N, col M):" prefix we prepend below.
message = stripAtPathPrefix(message)
// Translate schema constraint language (e.g. "minimum: got X, want Y") to plain English.
message = translateSchemaConstraintMessage(message)
@@ -377,5 +384,9 @@ func formatSchemaFailureDetail(pathInfo JSONPathInfo, schemaJSON, frontmatterCon
if suggestions != "" {
message = message + ". " + suggestions
}
- return fmt.Sprintf("at '%s' (line %d, column %d): %s", path, line, column, message)
+ displayPath := strings.TrimPrefix(path, "/")
+ if displayPath == "" {
+ return message
+ }
+ return fmt.Sprintf("'%s' (line %d, col %d): %s", displayPath, line, column, message)
}
diff --git a/pkg/parser/schema_deprecation.go b/pkg/parser/schema_deprecation.go
index 40974a1759c..2ca7a575f17 100644
--- a/pkg/parser/schema_deprecation.go
+++ b/pkg/parser/schema_deprecation.go
@@ -5,6 +5,7 @@ import (
"fmt"
"regexp"
"sort"
+ "sync"
)
// DeprecatedField represents a deprecated field with its replacement information
@@ -14,21 +15,37 @@ type DeprecatedField struct {
Description string // Description from the schema
}
-// GetMainWorkflowDeprecatedFields returns a list of deprecated fields from the main workflow schema
-func GetMainWorkflowDeprecatedFields() ([]DeprecatedField, error) {
- log.Print("Getting deprecated fields from main workflow schema")
- // Parse the schema JSON
- var schemaDoc map[string]any
- if err := json.Unmarshal([]byte(mainWorkflowSchema), &schemaDoc); err != nil {
- return nil, fmt.Errorf("failed to parse main workflow schema: %w", err)
- }
+// deprecatedFieldsCache caches the result of parsing the main workflow schema so that
+// the expensive 414KB JSON unmarshal is only performed once per process lifetime.
+// Both the result and any error are cached permanently: since mainWorkflowSchema is an
+// embedded compile-time constant, a parse failure is always a programming error (not
+// transient), so re-parsing on subsequent calls would produce the same failure.
+var (
+ deprecatedFieldsOnce sync.Once
+ deprecatedFieldsCache []DeprecatedField
+ deprecatedFieldsErr error
+)
- fields, err := extractDeprecatedFields(schemaDoc)
- if err != nil {
- return nil, err
- }
- log.Printf("Found %d deprecated fields in main workflow schema", len(fields))
- return fields, nil
+// GetMainWorkflowDeprecatedFields returns a list of deprecated fields from the main workflow schema.
+// The result is cached after the first call so the schema is only parsed once per process.
+// Callers must not modify the returned slice.
+func GetMainWorkflowDeprecatedFields() ([]DeprecatedField, error) {
+ deprecatedFieldsOnce.Do(func() {
+ log.Print("Getting deprecated fields from main workflow schema")
+ var schemaDoc map[string]any
+ if err := json.Unmarshal([]byte(mainWorkflowSchema), &schemaDoc); err != nil {
+ deprecatedFieldsErr = fmt.Errorf("failed to parse main workflow schema: %w", err)
+ return
+ }
+ fields, err := extractDeprecatedFields(schemaDoc)
+ if err != nil {
+ deprecatedFieldsErr = err
+ return
+ }
+ deprecatedFieldsCache = fields
+ log.Printf("Found %d deprecated fields in main workflow schema", len(fields))
+ })
+ return deprecatedFieldsCache, deprecatedFieldsErr
}
// extractDeprecatedFields extracts deprecated fields from a schema document
@@ -75,20 +92,21 @@ func extractDeprecatedFields(schemaDoc map[string]any) ([]DeprecatedField, error
return deprecated, nil
}
-// extractReplacementFromDescription extracts the replacement field name from a description
-// It looks for patterns like "Use 'field-name' instead" or "Deprecated: Use 'field-name'"
-func extractReplacementFromDescription(description string) string {
- // Common patterns in deprecation messages
- patterns := []string{
- `[Uu]se '([^']+)' instead`,
- `[Uu]se "([^"]+)" instead`,
- `[Uu]se ` + "`" + `([^` + "`" + `]+)` + "`" + ` instead`,
- `[Rr]eplace(?:d)? (?:with|by) '([^']+)'`,
- `[Rr]eplace(?:d)? (?:with|by) "([^"]+)"`,
- }
+// replacementPatterns are pre-compiled regexes used by extractReplacementFromDescription.
+// Pre-compiling avoids repeated compilation overhead when extracting replacements from
+// many deprecated field descriptions.
+var replacementPatterns = []*regexp.Regexp{
+ regexp.MustCompile(`[Uu]se '([^']+)' instead`),
+ regexp.MustCompile(`[Uu]se "([^"]+)" instead`),
+ regexp.MustCompile("[Uu]se `([^`]+)` instead"),
+ regexp.MustCompile(`[Rr]eplace(?:d)? (?:with|by) '([^']+)'`),
+ regexp.MustCompile(`[Rr]eplace(?:d)? (?:with|by) "([^"]+)"`),
+}
- for _, pattern := range patterns {
- re := regexp.MustCompile(pattern)
+// extractReplacementFromDescription extracts the replacement field name from a description.
+// It looks for patterns like "Use 'field-name' instead" or "Deprecated: Use 'field-name'".
+func extractReplacementFromDescription(description string) string {
+ for _, re := range replacementPatterns {
if match := re.FindStringSubmatch(description); len(match) >= 2 {
return match[1]
}
diff --git a/pkg/parser/schema_suggestions.go b/pkg/parser/schema_suggestions.go
index a35e1cb4928..3500a87f009 100644
--- a/pkg/parser/schema_suggestions.go
+++ b/pkg/parser/schema_suggestions.go
@@ -90,6 +90,19 @@ func generateSchemaBasedSuggestions(schemaJSON, errorMessage, jsonPath, frontmat
}
}
+ // Check if this is a minimum/maximum constraint violation and surface schema examples.
+ // pathInfo.Message has the form "at '/timeout-minutes': minimum: got X, want Y",
+ // so use Contains rather than HasPrefix to detect the constraint keyword.
+ lowerMsg := strings.ToLower(errorMessage)
+ if (strings.Contains(lowerMsg, "minimum:") || strings.Contains(lowerMsg, "maximum:")) &&
+ strings.Contains(lowerMsg, "got ") && strings.Contains(lowerMsg, "want ") {
+ schemaSuggestionsLog.Print("Detected range constraint violation, looking for schema examples")
+ if examples := extractSchemaExamples(schemaDoc, jsonPath); len(examples) > 0 {
+ schemaSuggestionsLog.Printf("Found %d schema examples for %s", len(examples), jsonPath)
+ return "Example values: " + strings.Join(examples, ", ")
+ }
+ }
+
// Check if this is a type error
if strings.Contains(strings.ToLower(errorMessage), "got ") && strings.Contains(strings.ToLower(errorMessage), "want ") {
schemaSuggestionsLog.Print("Detected type mismatch error")
@@ -104,6 +117,34 @@ func generateSchemaBasedSuggestions(schemaJSON, errorMessage, jsonPath, frontmat
return ""
}
+// extractSchemaExamples navigates the schema to the given JSON path using
+// navigateToSchemaPath and returns any "examples" array entries as formatted strings.
+// Returns nil if the path does not exist in the schema or the field has no examples.
+// The schema must have a top-level "properties" map for path navigation to succeed.
+func extractSchemaExamples(schemaDoc any, jsonPath string) []string {
+ schemaMap, ok := schemaDoc.(map[string]any)
+ if !ok {
+ return nil
+ }
+ target := navigateToSchemaPath(schemaMap, jsonPath)
+ if target == nil {
+ return nil
+ }
+ raw, ok := target["examples"]
+ if !ok {
+ return nil
+ }
+ items, ok := raw.([]any)
+ if !ok || len(items) == 0 {
+ return nil
+ }
+ examples := make([]string, 0, len(items))
+ for _, item := range items {
+ examples = append(examples, fmt.Sprintf("%v", item))
+ }
+ return examples
+}
+
// extractAcceptedFieldsFromSchema extracts the list of accepted fields from a schema at a given JSON path
func extractAcceptedFieldsFromSchema(schemaDoc any, jsonPath string) []string {
schemaMap, ok := schemaDoc.(map[string]any)
diff --git a/pkg/parser/schema_suggestions_test.go b/pkg/parser/schema_suggestions_test.go
index 8a140adbfc1..3c29c663756 100644
--- a/pkg/parser/schema_suggestions_test.go
+++ b/pkg/parser/schema_suggestions_test.go
@@ -854,3 +854,147 @@ func TestGenerateSchemaBasedSuggestionsWithPathHeuristic(t *testing.T) {
})
}
}
+
+// TestExtractSchemaExamples tests that examples are extracted from the schema for a given JSON path.
+func TestExtractSchemaExamples(t *testing.T) {
+ schemaJSON := `{
+ "type": "object",
+ "properties": {
+ "timeout-minutes": {
+ "type": "integer",
+ "minimum": 1,
+ "examples": [5, 10, 30]
+ },
+ "name": {
+ "type": "string",
+ "examples": ["My Workflow"]
+ },
+ "no-examples": {
+ "type": "integer"
+ }
+ }
+ }`
+ var schemaDoc any
+ if err := json.Unmarshal([]byte(schemaJSON), &schemaDoc); err != nil {
+ t.Fatalf("Failed to parse schema JSON: %v", err)
+ }
+
+ tests := []struct {
+ name string
+ jsonPath string
+ want []string
+ }{
+ {
+ name: "integer field with examples",
+ jsonPath: "/timeout-minutes",
+ want: []string{"5", "10", "30"},
+ },
+ {
+ name: "string field with examples",
+ jsonPath: "/name",
+ want: []string{"My Workflow"},
+ },
+ {
+ name: "field without examples",
+ jsonPath: "/no-examples",
+ want: nil,
+ },
+ {
+ name: "non-existent path",
+ jsonPath: "/does-not-exist",
+ want: nil,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := extractSchemaExamples(schemaDoc, tt.jsonPath)
+ if len(got) != len(tt.want) {
+ t.Errorf("extractSchemaExamples(%q) returned %v, want %v", tt.jsonPath, got, tt.want)
+ return
+ }
+ for i := range tt.want {
+ if got[i] != tt.want[i] {
+ t.Errorf("extractSchemaExamples(%q)[%d] = %q, want %q", tt.jsonPath, i, got[i], tt.want[i])
+ }
+ }
+ })
+ }
+}
+
+// TestGenerateSchemaBasedSuggestions_RangeConstraintExamples tests that examples are surfaced
+// for minimum/maximum constraint violations.
+// pathInfo.Message from jsonschema has the form "at '/path': minimum: got X, want Y".
+func TestGenerateSchemaBasedSuggestions_RangeConstraintExamples(t *testing.T) {
+ schemaJSON := `{
+ "type": "object",
+ "properties": {
+ "timeout-minutes": {
+ "type": "integer",
+ "minimum": 1,
+ "examples": [5, 10, 30]
+ },
+ "retry-count": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 10
+ }
+ }
+ }`
+
+ tests := []struct {
+ name string
+ errorMessage string
+ jsonPath string
+ wantContains string
+ wantEmpty bool
+ }{
+ {
+ name: "minimum violation with examples (bare form)",
+ errorMessage: "minimum: got -1, want 1",
+ jsonPath: "/timeout-minutes",
+ wantContains: "Example values: 5, 10, 30",
+ },
+ {
+ name: "minimum violation with examples (at-path prefix form from jsonschema)",
+ errorMessage: "at '/timeout-minutes': minimum: got -1, want 1",
+ jsonPath: "/timeout-minutes",
+ wantContains: "Example values: 5, 10, 30",
+ },
+ {
+ name: "maximum violation with examples surfaced",
+ errorMessage: "at '/timeout-minutes': maximum: got 15, want 10",
+ jsonPath: "/timeout-minutes",
+ wantContains: "Example values: 5, 10, 30",
+ },
+ {
+ name: "minimum violation without examples — range handler skips, type handler may fire",
+ errorMessage: "at '/retry-count': minimum: got -1, want 0",
+ jsonPath: "/retry-count",
+ // The range-constraint handler finds no examples and skips.
+ // The type-error handler may still produce a generic "Expected format: …" hint.
+ // What must NOT happen is surfacing "Example values: …" (there are none).
+ wantContains: "",
+ wantEmpty: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := generateSchemaBasedSuggestions(schemaJSON, tt.errorMessage, tt.jsonPath, "")
+ if tt.wantEmpty {
+ if result != "" {
+ t.Errorf("Expected empty result, got: %q", result)
+ }
+ return
+ }
+ if tt.wantContains != "" && !strings.Contains(result, tt.wantContains) {
+ t.Errorf("Expected result to contain %q, got: %q", tt.wantContains, result)
+ }
+ // When wantContains is "", we only assert "Example values" is absent (no false examples)
+ if tt.wantContains == "" && strings.Contains(result, "Example values:") {
+ t.Errorf("Expected result NOT to contain 'Example values:', got: %q", result)
+ }
+ })
+ }
+}
diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json
index bf72e65b56c..096121391a2 100644
--- a/pkg/parser/schemas/main_workflow_schema.json
+++ b/pkg/parser/schemas/main_workflow_schema.json
@@ -300,6 +300,86 @@
}
]
},
+ "label_command": {
+ "description": "On Label Command trigger: fires when a specific label is added to an issue, pull request, or discussion. The triggering label is automatically removed at workflow start so it can be applied again to re-trigger. Use the 'events' field to restrict which item types (issues, pull_request, discussion) activate the trigger.",
+ "oneOf": [
+ {
+ "type": "string",
+ "minLength": 1,
+ "description": "Label name as a string (shorthand format). The workflow fires when this label is added to any supported item type (issue, pull request, or discussion)."
+ },
+ {
+ "type": "object",
+ "description": "Label command configuration object with label name(s) and optional event filtering.",
+ "properties": {
+ "name": {
+ "oneOf": [
+ {
+ "type": "string",
+ "minLength": 1,
+ "description": "Single label name that acts as a command (e.g., 'deploy' triggers the workflow when the 'deploy' label is added)."
+ },
+ {
+ "type": "array",
+ "minItems": 1,
+ "description": "Array of label names \u2014 any of these labels will trigger the workflow.",
+ "items": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A label name"
+ },
+ "maxItems": 25
+ }
+ ],
+ "description": "Label name(s) that trigger the workflow when added to an issue, pull request, or discussion."
+ },
+ "names": {
+ "oneOf": [
+ {
+ "type": "string",
+ "minLength": 1,
+ "description": "Single label name."
+ },
+ {
+ "type": "array",
+ "minItems": 1,
+ "description": "Array of label names \u2014 any of these labels will trigger the workflow.",
+ "items": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A label name"
+ },
+ "maxItems": 25
+ }
+ ],
+ "description": "Alternative to 'name': label name(s) that trigger the workflow."
+ },
+ "events": {
+ "description": "Item types where the label-command trigger should be active. Default is all supported types: issues, pull_request, discussion.",
+ "oneOf": [
+ {
+ "type": "string",
+ "description": "Single item type or '*' for all types.",
+ "enum": ["*", "issues", "pull_request", "discussion"]
+ },
+ {
+ "type": "array",
+ "minItems": 1,
+ "description": "Array of item types where the trigger is active.",
+ "items": {
+ "type": "string",
+ "description": "Item type.",
+ "enum": ["*", "issues", "pull_request", "discussion"]
+ },
+ "maxItems": 3
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ]
+ },
"push": {
"description": "Push event trigger that runs the workflow when code is pushed to the repository",
"type": "object",
@@ -1418,12 +1498,12 @@
"description": "Skip workflow execution for specific GitHub users. Useful for preventing workflows from running for specific accounts (e.g., bots, specific team members)."
},
"roles": {
- "description": "Repository access roles required to trigger agentic workflows. Defaults to ['admin', 'maintainer', 'write'] for security. Use 'all' to allow any authenticated user (⚠️ security consideration).",
+ "description": "Repository access roles required to trigger agentic workflows. Defaults to ['admin', 'maintainer', 'write'] for security. Use 'all' to allow any authenticated user (\u26a0\ufe0f security consideration).",
"oneOf": [
{
"type": "string",
"enum": ["all"],
- "description": "Allow any authenticated user to trigger the workflow (⚠️ disables permission checking entirely - use with caution)"
+ "description": "Allow any authenticated user to trigger the workflow (\u26a0\ufe0f disables permission checking entirely - use with caution)"
},
{
"type": "array",
@@ -1509,6 +1589,131 @@
"private-key": "${{ secrets.APP_PRIVATE_KEY }}"
}
]
+ },
+ "steps": {
+ "type": "array",
+ "description": "Steps to inject into the pre-activation job. These steps run after all built-in checks (membership, stop-time, skip-if, etc.) and their results are exposed as pre-activation outputs. Use 'id' on steps to reference their results via needs.pre_activation.outputs._result.",
+ "items": {
+ "type": "object",
+ "description": "A GitHub Actions step to inject into the pre-activation job",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Optional name for the step"
+ },
+ "id": {
+ "type": "string",
+ "description": "Optional step ID. When set, the step result is exposed as needs.pre_activation.outputs._result"
+ },
+ "run": {
+ "type": "string",
+ "description": "Shell command to run"
+ },
+ "uses": {
+ "type": "string",
+ "description": "Action to use (e.g., 'actions/checkout@v4')"
+ },
+ "with": {
+ "type": "object",
+ "description": "Input parameters for the action",
+ "additionalProperties": true
+ },
+ "env": {
+ "type": "object",
+ "description": "Environment variables for the step",
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "if": {
+ "type": "string",
+ "description": "Conditional expression for the step"
+ },
+ "continue-on-error": {
+ "type": "boolean",
+ "description": "Whether to continue if the step fails"
+ }
+ },
+ "additionalProperties": true
+ },
+ "examples": [
+ [
+ {
+ "name": "Gate check",
+ "id": "gate",
+ "run": "echo 'Checking gate...' && exit 0"
+ }
+ ]
+ ]
+ },
+ "permissions": {
+ "description": "Additional permissions for the pre-activation job. Use to declare extra scopes required by on.steps (e.g., issues: read for GitHub API calls in steps).",
+ "oneOf": [
+ {
+ "type": "object",
+ "description": "Map of permission scope to level",
+ "properties": {
+ "actions": {
+ "type": "string",
+ "enum": ["read", "write", "none"]
+ },
+ "checks": {
+ "type": "string",
+ "enum": ["read", "write", "none"]
+ },
+ "contents": {
+ "type": "string",
+ "enum": ["read", "write", "none"]
+ },
+ "deployments": {
+ "type": "string",
+ "enum": ["read", "write", "none"]
+ },
+ "discussions": {
+ "type": "string",
+ "enum": ["read", "write", "none"]
+ },
+ "issues": {
+ "type": "string",
+ "enum": ["read", "write", "none"]
+ },
+ "packages": {
+ "type": "string",
+ "enum": ["read", "write", "none"]
+ },
+ "pages": {
+ "type": "string",
+ "enum": ["read", "write", "none"]
+ },
+ "pull-requests": {
+ "type": "string",
+ "enum": ["read", "write", "none"]
+ },
+ "repository-projects": {
+ "type": "string",
+ "enum": ["read", "write", "none"]
+ },
+ "security-events": {
+ "type": "string",
+ "enum": ["read", "write", "none"]
+ },
+ "statuses": {
+ "type": "string",
+ "enum": ["read", "write", "none"]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "examples": [
+ {
+ "issues": "read"
+ },
+ {
+ "issues": "read",
+ "pull-requests": "read"
+ }
+ ]
}
},
"additionalProperties": false,
@@ -2287,7 +2492,7 @@
},
"network": {
"$comment": "Strict mode requirements: When strict=true, the 'network' field must be present (not null/undefined) and cannot contain standalone wildcard '*' in allowed domains (but patterns like '*.example.com' ARE allowed). This is validated in Go code (pkg/workflow/strict_mode_validation.go) via validateStrictNetwork().",
- "description": "Network access control for AI engines using ecosystem identifiers and domain allowlists. Supports wildcard patterns like '*.example.com' to match any subdomain. Controls web fetch and search capabilities. IMPORTANT: For workflows that build/install/test code, always include the language ecosystem identifier alongside 'defaults' — 'defaults' alone only covers basic infrastructure, not package registries. Key ecosystem identifiers by runtime: 'dotnet' (.NET/NuGet), 'python' (pip/PyPI), 'node' (npm/yarn), 'go' (go modules), 'java' (Maven/Gradle), 'ruby' (Bundler), 'rust' (Cargo), 'swift' (Swift PM). Example: a .NET project needs network: { allowed: [defaults, dotnet] }.",
+ "description": "Network access control for AI engines using ecosystem identifiers and domain allowlists. Supports wildcard patterns like '*.example.com' to match any subdomain. Controls web fetch and search capabilities. IMPORTANT: For workflows that build/install/test code, always include the language ecosystem identifier alongside 'defaults' \u2014 'defaults' alone only covers basic infrastructure, not package registries. Key ecosystem identifiers by runtime: 'dotnet' (.NET/NuGet), 'python' (pip/PyPI), 'node' (npm/yarn), 'go' (go modules), 'java' (Maven/Gradle), 'ruby' (Bundler), 'rust' (Cargo), 'swift' (Swift PM). Example: a .NET project needs network: { allowed: [defaults, dotnet] }.",
"examples": [
"defaults",
{
@@ -2675,7 +2880,7 @@
]
},
"plugins": {
- "description": "⚠️ EXPERIMENTAL: Plugin configuration for installing plugins before workflow execution. Supports array format (list of repos/plugin configs) and object format (repos + custom token). Note: Plugin support is experimental and may change in future releases.",
+ "description": "\u26a0\ufe0f EXPERIMENTAL: Plugin configuration for installing plugins before workflow execution. Supports array format (list of repos/plugin configs) and object format (repos + custom token). Note: Plugin support is experimental and may change in future releases.",
"examples": [
["github/copilot-plugin", "acme/custom-tools"],
[
@@ -2872,7 +3077,7 @@
[
{
"name": "Verify Post-Steps Execution",
- "run": "echo \"✅ Post-steps are executing correctly\"\necho \"This step runs after the AI agent completes\"\n"
+ "run": "echo \"\u2705 Post-steps are executing correctly\"\necho \"This step runs after the AI agent completes\"\n"
},
{
"name": "Upload Test Results",
@@ -4136,7 +4341,7 @@
"properties": {
"allowed-domains": {
"type": "array",
- "description": "List of allowed domains for URI filtering in AI workflow output. URLs from other domains will be replaced with '(redacted)' for security.",
+ "description": "List of allowed domains for URL redaction in safe output handlers. Supports ecosystem identifiers (e.g., \"python\", \"node\", \"default-safe-outputs\") like network.allowed. These domains are unioned with the engine defaults and network.allowed when computing the final allowed domain set. localhost and github.com are always included.",
"items": {
"type": "string"
}
@@ -5363,7 +5568,7 @@
"items": {
"type": "string"
},
- "description": "Exclusive allowlist of glob patterns. When set, every file in the patch must match at least one pattern — files outside the list are always refused, including normal source files. This is a restriction, not an exception: setting allowed-files: [\".github/workflows/*\"] blocks all other files. To allow multiple sets of files, list all patterns explicitly. Acts independently of the protected-files policy; both checks must pass. To modify a protected file, it must both match allowed-files and be permitted by protected-files (e.g. protected-files: allowed). Supports * (any characters except /) and ** (any characters including /)."
+ "description": "Exclusive allowlist of glob patterns. When set, every file in the patch must match at least one pattern \u2014 files outside the list are always refused, including normal source files. This is a restriction, not an exception: setting allowed-files: [\".github/workflows/*\"] blocks all other files. To allow multiple sets of files, list all patterns explicitly. Acts independently of the protected-files policy; both checks must pass. To modify a protected file, it must both match allowed-files and be permitted by protected-files (e.g. protected-files: allowed). Supports * (any characters except /) and ** (any characters including /)."
},
"preserve-branch-name": {
"type": "boolean",
@@ -5578,7 +5783,7 @@
"oneOf": [
{
"type": "object",
- "description": "Configuration for resolving review threads on pull requests. Resolution is scoped to the triggering PR only — threads on other PRs cannot be resolved.",
+ "description": "Configuration for resolving review threads on pull requests. Resolution is scoped to the triggering PR only \u2014 threads on other PRs cannot be resolved.",
"properties": {
"max": {
"description": "Maximum number of review threads to resolve (default: 10) Supports integer or GitHub Actions expression (e.g. '${{ inputs.max }}').",
@@ -6427,7 +6632,7 @@
"items": {
"type": "string"
},
- "description": "Exclusive allowlist of glob patterns. When set, every file in the patch must match at least one pattern — files outside the list are always refused, including normal source files. This is a restriction, not an exception: setting allowed-files: [\".github/workflows/*\"] blocks all other files. To allow multiple sets of files, list all patterns explicitly. Acts independently of the protected-files policy; both checks must pass. To modify a protected file, it must both match allowed-files and be permitted by protected-files (e.g. protected-files: allowed). Supports * (any characters except /) and ** (any characters including /)."
+ "description": "Exclusive allowlist of glob patterns. When set, every file in the patch must match at least one pattern \u2014 files outside the list are always refused, including normal source files. This is a restriction, not an exception: setting allowed-files: [\".github/workflows/*\"] blocks all other files. To allow multiple sets of files, list all patterns explicitly. Acts independently of the protected-files policy; both checks must pass. To modify a protected file, it must both match allowed-files and be permitted by protected-files (e.g. protected-files: allowed). Supports * (any characters except /) and ** (any characters including /)."
},
"excluded-files": {
"type": "array",
@@ -7202,8 +7407,8 @@
},
"staged-title": {
"type": "string",
- "description": "Custom title template for staged mode preview. Available placeholders: {operation}. Example: '🎭 Preview: {operation}'",
- "examples": ["🎭 Preview: {operation}", "## Staged Mode: {operation}"]
+ "description": "Custom title template for staged mode preview. Available placeholders: {operation}. Example: '\ud83c\udfad Preview: {operation}'",
+ "examples": ["\ud83c\udfad Preview: {operation}", "## Staged Mode: {operation}"]
},
"staged-description": {
"type": "string",
@@ -7217,18 +7422,18 @@
},
"run-success": {
"type": "string",
- "description": "Custom message template for successful workflow completion. Available placeholders: {workflow_name}, {run_url}. Default: '✅ Agentic [{workflow_name}]({run_url}) completed successfully.'",
- "examples": ["✅ Agentic [{workflow_name}]({run_url}) completed successfully.", "✅ [{workflow_name}]({run_url}) finished."]
+ "description": "Custom message template for successful workflow completion. Available placeholders: {workflow_name}, {run_url}. Default: '\u2705 Agentic [{workflow_name}]({run_url}) completed successfully.'",
+ "examples": ["\u2705 Agentic [{workflow_name}]({run_url}) completed successfully.", "\u2705 [{workflow_name}]({run_url}) finished."]
},
"run-failure": {
"type": "string",
- "description": "Custom message template for failed workflow. Available placeholders: {workflow_name}, {run_url}, {status}. Default: '❌ Agentic [{workflow_name}]({run_url}) {status} and wasn't able to produce a result.'",
- "examples": ["❌ Agentic [{workflow_name}]({run_url}) {status} and wasn't able to produce a result.", "❌ [{workflow_name}]({run_url}) {status}."]
+ "description": "Custom message template for failed workflow. Available placeholders: {workflow_name}, {run_url}, {status}. Default: '\u274c Agentic [{workflow_name}]({run_url}) {status} and wasn't able to produce a result.'",
+ "examples": ["\u274c Agentic [{workflow_name}]({run_url}) {status} and wasn't able to produce a result.", "\u274c [{workflow_name}]({run_url}) {status}."]
},
"detection-failure": {
"type": "string",
- "description": "Custom message template for detection job failure. Available placeholders: {workflow_name}, {run_url}. Default: '⚠️ Security scanning failed for [{workflow_name}]({run_url}). Review the logs for details.'",
- "examples": ["⚠️ Security scanning failed for [{workflow_name}]({run_url}). Review the logs for details.", "⚠️ Detection job failed in [{workflow_name}]({run_url})."]
+ "description": "Custom message template for detection job failure. Available placeholders: {workflow_name}, {run_url}. Default: '\u26a0\ufe0f Security scanning failed for [{workflow_name}]({run_url}). Review the logs for details.'",
+ "examples": ["\u26a0\ufe0f Security scanning failed for [{workflow_name}]({run_url}). Review the logs for details.", "\u26a0\ufe0f Detection job failed in [{workflow_name}]({run_url})."]
},
"agent-failure-issue": {
"type": "string",
@@ -7799,6 +8004,35 @@
"isolated": {
"type": "boolean",
"description": "If true, agent restore step clears primitive dirs before unpacking."
+ },
+ "github-app": {
+ "type": "object",
+ "description": "GitHub App credentials for minting installation access tokens used by APM to access cross-org private repositories.",
+ "required": ["app-id", "private-key"],
+ "properties": {
+ "app-id": {
+ "type": "string",
+ "description": "GitHub App ID (e.g., '${{ vars.APP_ID }}').",
+ "examples": ["${{ vars.APP_ID }}"]
+ },
+ "private-key": {
+ "type": "string",
+ "description": "GitHub App private key (e.g., '${{ secrets.APP_PRIVATE_KEY }}').",
+ "examples": ["${{ secrets.APP_PRIVATE_KEY }}"]
+ },
+ "owner": {
+ "type": "string",
+ "description": "Optional owner of the GitHub App installation. Defaults to current repository owner if not specified."
+ },
+ "repositories": {
+ "type": "array",
+ "description": "Optional list of repositories to grant access to. Use [\"*\"] for all repositories in the installation owner's org.",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "additionalProperties": false
}
},
"required": ["packages"],
@@ -8369,7 +8603,7 @@
},
"auth": {
"type": "array",
- "description": "Authentication bindings — maps logical roles (e.g. 'api-key') to GitHub Actions secret names",
+ "description": "Authentication bindings \u2014 maps logical roles (e.g. 'api-key') to GitHub Actions secret names",
"items": {
"type": "object",
"properties": {
diff --git a/pkg/workflow/agentic_output_test.go b/pkg/workflow/agentic_output_test.go
index fcf25bbd5c3..56855e77a3c 100644
--- a/pkg/workflow/agentic_output_test.go
+++ b/pkg/workflow/agentic_output_test.go
@@ -62,8 +62,8 @@ This workflow tests the agentic output collection functionality.
lockContent := string(content)
// Verify GH_AW_SAFE_OUTPUTS is set at job level with fixed path
- if !strings.Contains(lockContent, "GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl") {
- t.Error("Expected 'GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl' environment variable in generated workflow")
+ if !strings.Contains(lockContent, "GH_AW_SAFE_OUTPUTS: "+GhAwHomeExpr+"/safeoutputs/outputs.jsonl") {
+ t.Error("Expected 'GH_AW_SAFE_OUTPUTS: " + GhAwHomeExpr + "/safeoutputs/outputs.jsonl' environment variable in generated workflow")
}
if !strings.Contains(lockContent, "- name: Ingest agent output") {
@@ -171,8 +171,8 @@ This workflow tests that Codex engine gets GH_AW_SAFE_OUTPUTS but not engine out
lockContent := string(content)
// Verify that Codex workflow DOES have GH_AW_SAFE_OUTPUTS functionality at job level
- if !strings.Contains(lockContent, "GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl") {
- t.Error("Codex workflow should have 'GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl' environment variable (GH_AW_SAFE_OUTPUTS functionality)")
+ if !strings.Contains(lockContent, "GH_AW_SAFE_OUTPUTS: "+GhAwHomeExpr+"/safeoutputs/outputs.jsonl") {
+ t.Error("Codex workflow should have 'GH_AW_SAFE_OUTPUTS: " + GhAwHomeExpr + "/safeoutputs/outputs.jsonl' environment variable (GH_AW_SAFE_OUTPUTS functionality)")
}
if !strings.Contains(lockContent, "- name: Ingest agent output") {
diff --git a/pkg/workflow/agentic_workflow_test.go b/pkg/workflow/agentic_workflow_test.go
index 1120317735e..fa8f66eadae 100644
--- a/pkg/workflow/agentic_workflow_test.go
+++ b/pkg/workflow/agentic_workflow_test.go
@@ -162,8 +162,8 @@ func TestAgenticWorkflowsInstallStepIncludesGHToken(t *testing.T) {
"install step should include command to verify gh-aw installation")
// Verify the binary copy command is present for MCP server containerization
- assert.Contains(t, result, "cp \"$GH_AW_BIN\" /opt/gh-aw/gh-aw",
- "install step should copy gh-aw binary to /opt/gh-aw for MCP server containerization")
+ assert.Contains(t, result, "cp \"$GH_AW_BIN\" "+GhAwHome+"/gh-aw",
+ "install step should copy gh-aw binary to "+GhAwHome+" for MCP server containerization")
}
func TestAgenticWorkflowsInstallStepSkippedWithImport(t *testing.T) {
diff --git a/pkg/workflow/allowed_domains_sanitization_test.go b/pkg/workflow/allowed_domains_sanitization_test.go
index 62fb3af390e..3fc01855c77 100644
--- a/pkg/workflow/allowed_domains_sanitization_test.go
+++ b/pkg/workflow/allowed_domains_sanitization_test.go
@@ -5,6 +5,7 @@ package workflow
import (
"os"
"path/filepath"
+ "slices"
"strings"
"testing"
@@ -13,6 +14,22 @@ import (
"github.com/github/gh-aw/pkg/testutil"
)
+// extractQuotedCSV returns the comma-separated domain list embedded inside
+// the first pair of double-quotes in line. Used to enable exact-entry checks
+// (avoiding substring false-positives like "corp.example.com" matching "copilot.corp.example.com").
+func extractQuotedCSV(line string) string {
+ start := strings.Index(line, `"`)
+ if start < 0 {
+ return line
+ }
+ rest := line[start+1:]
+ end := strings.Index(rest, `"`)
+ if end < 0 {
+ return rest
+ }
+ return rest[:end]
+}
+
// TestAllowedDomainsFromNetworkConfig tests that GH_AW_ALLOWED_DOMAINS is computed
// from network configuration for sanitization
func TestAllowedDomainsFromNetworkConfig(t *testing.T) {
@@ -238,9 +255,9 @@ Test workflow with ecosystem identifiers.
}
}
-// TestManualAllowedDomainsHasPriority tests that manually configured allowed-domains
-// takes precedence over network configuration
-func TestManualAllowedDomainsHasPriority(t *testing.T) {
+// TestManualAllowedDomainsUnionWithNetworkConfig tests that manually configured allowed-domains
+// unions with network configuration (not overrides it)
+func TestManualAllowedDomainsUnionWithNetworkConfig(t *testing.T) {
tests := []struct {
name string
workflow string
@@ -248,7 +265,7 @@ func TestManualAllowedDomainsHasPriority(t *testing.T) {
unexpectedDomain string
}{
{
- name: "Manual allowed-domains overrides network config",
+ name: "Manual allowed-domains unions with network config",
workflow: `---
on: push
permissions:
@@ -270,14 +287,15 @@ safe-outputs:
# Test Workflow
-Test that manual allowed-domains takes precedence.
+Test that manual allowed-domains unions with network config.
`,
expectedDomains: []string{
"manual-domain.com",
"override.org",
+ "example.com", // from network.allowed - still present (union)
},
- // Network domains and Copilot defaults should NOT be included
- unexpectedDomain: "example.com",
+ // No domain should be absent
+ unexpectedDomain: "",
},
{
name: "Empty allowed-domains uses network config",
@@ -373,10 +391,12 @@ Test that empty allowed-domains falls back to network config.
// TestComputeAllowedDomainsForSanitization tests the computeAllowedDomainsForSanitization function
func TestComputeAllowedDomainsForSanitization(t *testing.T) {
tests := []struct {
- name string
- engineID string
- networkPerms *NetworkPermissions
- expectedDomains []string
+ name string
+ engineID string
+ apiTarget string
+ networkPerms *NetworkPermissions
+ expectedDomains []string
+ unexpectedDomains []string
}{
{
name: "Copilot with custom domains",
@@ -433,6 +453,30 @@ func TestComputeAllowedDomainsForSanitization(t *testing.T) {
"example.com",
},
},
+ {
+ name: "Copilot with GHES api-target includes api and base domains",
+ engineID: "copilot",
+ apiTarget: "api.acme.ghe.com",
+ networkPerms: nil,
+ expectedDomains: []string{
+ "api.acme.ghe.com", // GHES API domain
+ "acme.ghe.com", // GHES base domain (derived from api-target)
+ "api.github.com", // Copilot default
+ "github.com", // Copilot default
+ },
+ },
+ {
+ name: "non-api prefix api-target only adds the configured hostname",
+ engineID: "copilot",
+ apiTarget: "copilot.corp.example.com",
+ networkPerms: nil,
+ expectedDomains: []string{
+ "copilot.corp.example.com", // configured hostname
+ },
+ unexpectedDomains: []string{
+ "corp.example.com", // base hostname should NOT be added for non-api. prefix
+ },
+ },
}
for _, tt := range tests {
@@ -441,7 +485,8 @@ func TestComputeAllowedDomainsForSanitization(t *testing.T) {
compiler := NewCompiler()
data := &WorkflowData{
EngineConfig: &EngineConfig{
- ID: tt.engineID,
+ ID: tt.engineID,
+ APITarget: tt.apiTarget,
},
NetworkPermissions: tt.networkPerms,
}
@@ -449,12 +494,452 @@ func TestComputeAllowedDomainsForSanitization(t *testing.T) {
// Call the function
domainsStr := compiler.computeAllowedDomainsForSanitization(data)
- // Verify expected domains are present
+ // Verify expected domains are present (substring match is fine here since domain names
+ // in a CSV string that are exact entries won't appear as substrings of other entries
+ // when checking expected ones – we only need exact match for the negative "not present" check)
for _, expectedDomain := range tt.expectedDomains {
if !strings.Contains(domainsStr, expectedDomain) {
t.Errorf("Expected domain '%s' not found in result: %s", expectedDomain, domainsStr)
}
}
+
+ // Verify unexpected domains are absent using exact membership (not substring)
+ // to avoid false positives where "corp.example.com" matches "copilot.corp.example.com"
+ parts := strings.Split(domainsStr, ",")
+ for _, unexpectedDomain := range tt.unexpectedDomains {
+ if slices.Contains(parts, unexpectedDomain) {
+ t.Errorf("Unexpected domain '%s' found in result: %s", unexpectedDomain, domainsStr)
+ }
+ }
+ })
+ }
+}
+
+// TestAPITargetDomainsInCompiledWorkflow is a regression test verifying that when engine.api-target
+// is configured, both --allow-domains (AWF firewall flag) and GH_AW_ALLOWED_DOMAINS (sanitization
+// env var) in the compiled lock file contain the api-target hostname and its derived base hostname.
+func TestAPITargetDomainsInCompiledWorkflow(t *testing.T) {
+ tests := []struct {
+ name string
+ workflow string
+ expectedDomains []string
+ unexpectedDomains []string
+ }{
+ {
+ name: "GHES api-target adds api and base domains to allow-domains and GH_AW_ALLOWED_DOMAINS",
+ workflow: `---
+on: push
+permissions:
+ contents: read
+ issues: read
+ pull-requests: read
+engine:
+ id: copilot
+ api-target: api.acme.ghe.com
+strict: false
+safe-outputs:
+ create-issue:
+---
+
+# Test Workflow
+
+Test workflow with GHES api-target.
+`,
+ expectedDomains: []string{
+ "api.acme.ghe.com", // GHES API domain
+ "acme.ghe.com", // GHES base domain derived from api-target
+ "api.github.com", // Copilot default
+ "github.com", // Copilot default
+ },
+ },
+ {
+ name: "non-api prefix api-target only adds the configured hostname",
+ workflow: `---
+on: push
+permissions:
+ contents: read
+ issues: read
+ pull-requests: read
+engine:
+ id: copilot
+ api-target: copilot.corp.example.com
+strict: false
+safe-outputs:
+ create-issue:
+---
+
+# Test Workflow
+
+Test workflow with non-api prefix api-target.
+`,
+ expectedDomains: []string{
+ "copilot.corp.example.com", // configured hostname
+ },
+ unexpectedDomains: []string{
+ "corp.example.com", // base hostname should NOT be added for non-api. prefix
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tmpDir := testutil.TempDir(t, "api-target-domains-test")
+ testFile := filepath.Join(tmpDir, "test-workflow.md")
+ if err := os.WriteFile(testFile, []byte(tt.workflow), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ compiler := NewCompiler()
+ if err := compiler.CompileWorkflow(testFile); err != nil {
+ t.Fatalf("Failed to compile workflow: %v", err)
+ }
+
+ lockFile := stringutil.MarkdownToLockFile(testFile)
+ lockContent, err := os.ReadFile(lockFile)
+ if err != nil {
+ t.Fatalf("Failed to read lock file: %v", err)
+ }
+ lockStr := string(lockContent)
+
+ // Check --allow-domains in AWF command contains expected domains
+ allowDomainsIdx := strings.Index(lockStr, "--allow-domains")
+ if allowDomainsIdx < 0 {
+ t.Fatal("--allow-domains flag not found in compiled lock file")
+ }
+ // Extract the line with --allow-domains for more targeted checking
+ allowDomainsEnd := strings.Index(lockStr[allowDomainsIdx:], "\n")
+ if allowDomainsEnd < 0 {
+ allowDomainsEnd = len(lockStr) - allowDomainsIdx
+ }
+ allowDomainsLine := lockStr[allowDomainsIdx : allowDomainsIdx+allowDomainsEnd]
+
+ for _, domain := range tt.expectedDomains {
+ if !strings.Contains(allowDomainsLine, domain) {
+ t.Errorf("Expected domain %q not found in --allow-domains.\nLine: %s", domain, allowDomainsLine)
+ }
+ }
+ // Use exact CSV membership for "not present" checks to avoid false positives
+ // (e.g. "corp.example.com" would substring-match "copilot.corp.example.com")
+ allowedDomainsCSV := extractQuotedCSV(allowDomainsLine)
+ allowedParts := strings.Split(allowedDomainsCSV, ",")
+ for _, domain := range tt.unexpectedDomains {
+ if slices.Contains(allowedParts, domain) {
+ t.Errorf("Unexpected domain %q found in --allow-domains.\nLine: %s", domain, allowDomainsLine)
+ }
+ }
+
+ // Check GH_AW_ALLOWED_DOMAINS env var contains expected domains
+ lines := strings.Split(lockStr, "\n")
+ var domainsLine string
+ for _, line := range lines {
+ if strings.Contains(line, "GH_AW_ALLOWED_DOMAINS:") {
+ domainsLine = line
+ break
+ }
+ }
+ if domainsLine == "" {
+ t.Fatal("GH_AW_ALLOWED_DOMAINS not found in compiled lock file")
+ }
+
+ for _, domain := range tt.expectedDomains {
+ if !strings.Contains(domainsLine, domain) {
+ t.Errorf("Expected domain %q not found in GH_AW_ALLOWED_DOMAINS.\nLine: %s", domain, domainsLine)
+ }
+ }
+ // Use exact CSV membership for "not present" checks
+ allowedDomainsEnvCSV := extractQuotedCSV(domainsLine)
+ allowedEnvParts := strings.Split(allowedDomainsEnvCSV, ",")
+ for _, domain := range tt.unexpectedDomains {
+ if slices.Contains(allowedEnvParts, domain) {
+ t.Errorf("Unexpected domain %q found in GH_AW_ALLOWED_DOMAINS.\nLine: %s", domain, domainsLine)
+ }
+ }
+ })
+ }
+}
+
+// TestAllowedDomainsUnionWithNetworkConfig tests that safe-outputs.allowed-domains
+// is unioned with network.allowed and always includes localhost and github.com
+func TestAllowedDomainsUnionWithNetworkConfig(t *testing.T) {
+ tests := []struct {
+ name string
+ workflow string
+ expectedDomains []string
+ }{
+ {
+ name: "allowed-domains unioned with Copilot defaults and network config",
+ workflow: `---
+on: push
+permissions:
+ contents: read
+ issues: read
+engine: copilot
+strict: false
+network:
+ allowed:
+ - example.com
+safe-outputs:
+ create-issue:
+ allowed-domains:
+ - extra-domain.com
+---
+
+# Test Workflow
+
+Test allowed-domains union with network config.
+`,
+ expectedDomains: []string{
+ "extra-domain.com", // from allowed-domains
+ "example.com", // from network.allowed
+ "api.github.com", // Copilot default
+ "localhost", // always included
+ "github.com", // always included
+ },
+ },
+ {
+ name: "allowed-domains supports ecosystem identifiers",
+ workflow: `---
+on: push
+permissions:
+ contents: read
+ issues: read
+engine: copilot
+strict: false
+safe-outputs:
+ create-issue:
+ allowed-domains:
+ - dev-tools
+ - python
+---
+
+# Test Workflow
+
+Test allowed-domains with ecosystem identifiers.
+`,
+ expectedDomains: []string{
+ "codecov.io", // from dev-tools ecosystem
+ "snyk.io", // from dev-tools ecosystem
+ "pypi.org", // from python ecosystem
+ "localhost", // always included
+ "github.com", // always included
+ },
+ },
+ {
+ name: "allowed-domains does not override network config",
+ workflow: `---
+on: push
+permissions:
+ contents: read
+ issues: read
+engine: copilot
+strict: false
+network:
+ allowed:
+ - network-domain.com
+safe-outputs:
+ create-issue:
+ allowed-domains:
+ - url-domain.com
+---
+
+# Test Workflow
+
+Test that allowed-domains does not override network config.
+`,
+ expectedDomains: []string{
+ "url-domain.com", // from allowed-domains
+ "network-domain.com", // from network.allowed - still present (union)
+ "api.github.com", // Copilot default
+ "localhost", // always included
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tmpDir := testutil.TempDir(t, "allowed-domains-test")
+ testFile := filepath.Join(tmpDir, "test-workflow.md")
+ if err := os.WriteFile(testFile, []byte(tt.workflow), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ compiler := NewCompiler()
+ if err := compiler.CompileWorkflow(testFile); err != nil {
+ t.Fatalf("Failed to compile workflow: %v", err)
+ }
+
+ lockFile := stringutil.MarkdownToLockFile(testFile)
+ lockContent, err := os.ReadFile(lockFile)
+ if err != nil {
+ t.Fatalf("Failed to read lock file: %v", err)
+ }
+ lockStr := string(lockContent)
+
+ if !strings.Contains(lockStr, "GH_AW_ALLOWED_DOMAINS:") {
+ t.Error("Expected GH_AW_ALLOWED_DOMAINS environment variable in lock file")
+ }
+
+ lines := strings.Split(lockStr, "\n")
+ var domainsLine string
+ for _, line := range lines {
+ if strings.Contains(line, "GH_AW_ALLOWED_DOMAINS:") {
+ domainsLine = line
+ break
+ }
+ }
+
+ if domainsLine == "" {
+ t.Fatal("GH_AW_ALLOWED_DOMAINS not found in lock file")
+ }
+
+ for _, expectedDomain := range tt.expectedDomains {
+ if !strings.Contains(domainsLine, expectedDomain) {
+ t.Errorf("Expected domain %q not found in GH_AW_ALLOWED_DOMAINS.\nLine: %s", expectedDomain, domainsLine)
+ }
+ }
+ })
+ }
+}
+
+// TestAllowedDomainsUnionWithNetworkConfig tests that safe-outputs.allowed-domains
+// is unioned with network.allowed and always includes localhost and github.com
+func TestAllowedDomainsUnionWithNetworkConfig(t *testing.T) {
+ tests := []struct {
+ name string
+ workflow string
+ expectedDomains []string
+ }{
+ {
+ name: "allowed-domains unioned with Copilot defaults and network config",
+ workflow: `---
+on: push
+permissions:
+ contents: read
+ issues: read
+engine: copilot
+strict: false
+network:
+ allowed:
+ - example.com
+safe-outputs:
+ create-issue:
+ allowed-domains:
+ - extra-domain.com
+---
+
+# Test Workflow
+
+Test allowed-domains union with network config.
+`,
+ expectedDomains: []string{
+ "extra-domain.com", // from allowed-domains
+ "example.com", // from network.allowed
+ "api.github.com", // Copilot default
+ "localhost", // always included
+ "github.com", // always included
+ },
+ },
+ {
+ name: "allowed-domains supports ecosystem identifiers",
+ workflow: `---
+on: push
+permissions:
+ contents: read
+ issues: read
+engine: copilot
+strict: false
+safe-outputs:
+ create-issue:
+ allowed-domains:
+ - dev-tools
+ - python
+---
+
+# Test Workflow
+
+Test allowed-domains with ecosystem identifiers.
+`,
+ expectedDomains: []string{
+ "codecov.io", // from dev-tools ecosystem
+ "snyk.io", // from dev-tools ecosystem
+ "pypi.org", // from python ecosystem
+ "localhost", // always included
+ "github.com", // always included
+ },
+ },
+ {
+ name: "allowed-domains does not override network config",
+ workflow: `---
+on: push
+permissions:
+ contents: read
+ issues: read
+engine: copilot
+strict: false
+network:
+ allowed:
+ - network-domain.com
+safe-outputs:
+ create-issue:
+ allowed-domains:
+ - url-domain.com
+---
+
+# Test Workflow
+
+Test that allowed-domains does not override network config.
+`,
+ expectedDomains: []string{
+ "url-domain.com", // from allowed-domains
+ "network-domain.com", // from network.allowed - still present (union)
+ "api.github.com", // Copilot default
+ "localhost", // always included
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tmpDir := testutil.TempDir(t, "allowed-domains-test")
+ testFile := filepath.Join(tmpDir, "test-workflow.md")
+ if err := os.WriteFile(testFile, []byte(tt.workflow), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ compiler := NewCompiler()
+ if err := compiler.CompileWorkflow(testFile); err != nil {
+ t.Fatalf("Failed to compile workflow: %v", err)
+ }
+
+ lockFile := stringutil.MarkdownToLockFile(testFile)
+ lockContent, err := os.ReadFile(lockFile)
+ if err != nil {
+ t.Fatalf("Failed to read lock file: %v", err)
+ }
+ lockStr := string(lockContent)
+
+ if !strings.Contains(lockStr, "GH_AW_ALLOWED_DOMAINS:") {
+ t.Error("Expected GH_AW_ALLOWED_DOMAINS environment variable in lock file")
+ }
+
+ lines := strings.Split(lockStr, "\n")
+ var domainsLine string
+ for _, line := range lines {
+ if strings.Contains(line, "GH_AW_ALLOWED_DOMAINS:") {
+ domainsLine = line
+ break
+ }
+ }
+
+ if domainsLine == "" {
+ t.Fatal("GH_AW_ALLOWED_DOMAINS not found in lock file")
+ }
+
+ for _, expectedDomain := range tt.expectedDomains {
+ if !strings.Contains(domainsLine, expectedDomain) {
+ t.Errorf("Expected domain %q not found in GH_AW_ALLOWED_DOMAINS.\nLine: %s", expectedDomain, domainsLine)
+ }
+ }
})
}
}
diff --git a/pkg/workflow/apm_dependencies.go b/pkg/workflow/apm_dependencies.go
index f4b625be7c7..ab80004a5da 100644
--- a/pkg/workflow/apm_dependencies.go
+++ b/pkg/workflow/apm_dependencies.go
@@ -1,11 +1,81 @@
package workflow
import (
+ "fmt"
+ "sort"
+
"github.com/github/gh-aw/pkg/logger"
)
var apmDepsLog = logger.New("workflow:apm_dependencies")
+// apmAppTokenStepID is the step ID for the GitHub App token mint step used by APM dependencies.
+const apmAppTokenStepID = "apm-app-token"
+
+// buildAPMAppTokenMintStep generates the step to mint a GitHub App installation access token
+// for use by the APM pack step to access cross-org private repositories.
+//
+// Parameters:
+// - app: GitHub App configuration containing app-id, private-key, owner, and repositories
+// - fallbackRepoExpr: expression used as the repositories value when app.Repositories is empty.
+// Pass "${{ steps.resolve-host-repo.outputs.target_repo_name }}" for workflow_call relay
+// workflows so the token is scoped to the platform (host) repo rather than the caller repo.
+// Pass "" to use the default "${{ github.event.repository.name }}" fallback.
+//
+// Returns a slice of YAML step lines.
+func buildAPMAppTokenMintStep(app *GitHubAppConfig, fallbackRepoExpr string) []string {
+ apmDepsLog.Printf("Building APM GitHub App token mint step: owner=%s, repos=%d", app.Owner, len(app.Repositories))
+ var steps []string
+
+ steps = append(steps, " - name: Generate GitHub App token for APM dependencies\n")
+ steps = append(steps, fmt.Sprintf(" id: %s\n", apmAppTokenStepID))
+ steps = append(steps, fmt.Sprintf(" uses: %s\n", GetActionPin("actions/create-github-app-token")))
+ steps = append(steps, " with:\n")
+ steps = append(steps, fmt.Sprintf(" app-id: %s\n", app.AppID))
+ steps = append(steps, fmt.Sprintf(" private-key: %s\n", app.PrivateKey))
+
+ // Add owner - default to current repository owner if not specified
+ owner := app.Owner
+ if owner == "" {
+ owner = "${{ github.repository_owner }}"
+ }
+ steps = append(steps, fmt.Sprintf(" owner: %s\n", owner))
+
+ // Add repositories - behavior depends on configuration:
+ // - If repositories is ["*"], omit the field to allow org-wide access
+ // - If repositories is a single value, use inline format
+ // - If repositories has multiple values, use block scalar format
+ // - If repositories is empty/not specified, default to the current repository
+ if len(app.Repositories) == 1 && app.Repositories[0] == "*" {
+ // Org-wide access: omit repositories field entirely
+ apmDepsLog.Print("Using org-wide GitHub App token for APM (repositories: *)")
+ } else if len(app.Repositories) == 1 {
+ steps = append(steps, fmt.Sprintf(" repositories: %s\n", app.Repositories[0]))
+ } else if len(app.Repositories) > 1 {
+ steps = append(steps, " repositories: |-\n")
+ reposCopy := make([]string, len(app.Repositories))
+ copy(reposCopy, app.Repositories)
+ sort.Strings(reposCopy)
+ for _, repo := range reposCopy {
+ steps = append(steps, fmt.Sprintf(" %s\n", repo))
+ }
+ } else {
+ // No explicit repositories: use fallback expression, or default to the triggering repo's name.
+ // For workflow_call relay scenarios the caller passes steps.resolve-host-repo.outputs.target_repo_name
+ // so the token is scoped to the platform (host) repo name rather than the full owner/repo slug.
+ repoExpr := fallbackRepoExpr
+ if repoExpr == "" {
+ repoExpr = "${{ github.event.repository.name }}"
+ }
+ steps = append(steps, fmt.Sprintf(" repositories: %s\n", repoExpr))
+ }
+
+ // Always add github-api-url from environment variable
+ steps = append(steps, " github-api-url: ${{ github.api_url }}\n")
+
+ return steps
+}
+
// GenerateAPMPackStep generates the GitHub Actions step that installs APM packages and
// packs them into a bundle in the activation job. The step always uses isolated:true because
// the activation job has no repo context to preserve.
@@ -30,9 +100,20 @@ func GenerateAPMPackStep(apmDeps *APMDependenciesInfo, target string, data *Work
" - name: Install and pack APM dependencies",
" id: apm_pack",
" uses: " + actionRef,
+ }
+
+ // Inject the minted GitHub App token as GITHUB_TOKEN so APM can access cross-org private repos
+ if apmDeps.GitHubApp != nil {
+ lines = append(lines,
+ " env:",
+ fmt.Sprintf(" GITHUB_TOKEN: ${{ steps.%s.outputs.token }}", apmAppTokenStepID),
+ )
+ }
+
+ lines = append(lines,
" with:",
" dependencies: |",
- }
+ )
for _, dep := range apmDeps.Packages {
lines = append(lines, " - "+dep)
@@ -44,6 +125,7 @@ func GenerateAPMPackStep(apmDeps *APMDependenciesInfo, target string, data *Work
" archive: 'true'",
" target: "+target,
" working-directory: /tmp/gh-aw/apm-workspace",
+ " apm-version: ${{ env.GH_AW_INFO_APM_VERSION }}",
)
return GitHubActionStep(lines)
@@ -72,6 +154,7 @@ func GenerateAPMRestoreStep(apmDeps *APMDependenciesInfo, data *WorkflowData) Gi
" uses: " + actionRef,
" with:",
" bundle: /tmp/gh-aw/apm-bundle/*.tar.gz",
+ " apm-version: ${{ env.GH_AW_INFO_APM_VERSION }}",
}
if apmDeps.Isolated {
diff --git a/pkg/workflow/apm_dependencies_test.go b/pkg/workflow/apm_dependencies_test.go
index 77ab336218c..4cd81668ee0 100644
--- a/pkg/workflow/apm_dependencies_test.go
+++ b/pkg/workflow/apm_dependencies_test.go
@@ -10,6 +10,15 @@ import (
"github.com/stretchr/testify/require"
)
+// combineStepLines joins a GitHubActionStep slice into a single string for assertion.
+func combineStepLines(step []string) string {
+ var sb strings.Builder
+ for _, line := range step {
+ sb.WriteString(line + "\n")
+ }
+ return sb.String()
+}
+
func TestExtractAPMDependenciesFromFrontmatter(t *testing.T) {
tests := []struct {
name string
@@ -127,6 +136,102 @@ func TestExtractAPMDependenciesFromFrontmatter(t *testing.T) {
}
}
+func TestExtractAPMDependenciesGitHubApp(t *testing.T) {
+ t.Run("Object format with github-app", func(t *testing.T) {
+ frontmatter := map[string]any{
+ "dependencies": map[string]any{
+ "packages": []any{"acme-org/acme-skills/plugins/dev-tools"},
+ "github-app": map[string]any{
+ "app-id": "${{ vars.APP_ID }}",
+ "private-key": "${{ secrets.APP_PRIVATE_KEY }}",
+ },
+ },
+ }
+ result := extractAPMDependenciesFromFrontmatter(frontmatter)
+ require.NotNil(t, result, "Should return non-nil APMDependenciesInfo")
+ assert.Equal(t, []string{"acme-org/acme-skills/plugins/dev-tools"}, result.Packages)
+ require.NotNil(t, result.GitHubApp, "GitHubApp should be set")
+ assert.Equal(t, "${{ vars.APP_ID }}", result.GitHubApp.AppID)
+ assert.Equal(t, "${{ secrets.APP_PRIVATE_KEY }}", result.GitHubApp.PrivateKey)
+ })
+
+ t.Run("Object format with github-app including owner and repositories", func(t *testing.T) {
+ frontmatter := map[string]any{
+ "dependencies": map[string]any{
+ "packages": []any{"acme-org/acme-skills/plugins/dev-tools"},
+ "github-app": map[string]any{
+ "app-id": "${{ vars.APP_ID }}",
+ "private-key": "${{ secrets.APP_PRIVATE_KEY }}",
+ "owner": "acme-org",
+ "repositories": []any{"acme-skills"},
+ },
+ },
+ }
+ result := extractAPMDependenciesFromFrontmatter(frontmatter)
+ require.NotNil(t, result, "Should return non-nil APMDependenciesInfo")
+ require.NotNil(t, result.GitHubApp, "GitHubApp should be set")
+ assert.Equal(t, "acme-org", result.GitHubApp.Owner)
+ assert.Equal(t, []string{"acme-skills"}, result.GitHubApp.Repositories)
+ })
+
+ t.Run("Object format with github-app missing app-id is ignored", func(t *testing.T) {
+ frontmatter := map[string]any{
+ "dependencies": map[string]any{
+ "packages": []any{"acme-org/acme-skills"},
+ "github-app": map[string]any{
+ "private-key": "${{ secrets.APP_PRIVATE_KEY }}",
+ },
+ },
+ }
+ result := extractAPMDependenciesFromFrontmatter(frontmatter)
+ require.NotNil(t, result, "Packages should still be extracted")
+ assert.Nil(t, result.GitHubApp, "GitHubApp should be nil when app-id is missing")
+ })
+
+ t.Run("Array format does not support github-app", func(t *testing.T) {
+ frontmatter := map[string]any{
+ "dependencies": []any{"microsoft/apm-sample-package"},
+ }
+ result := extractAPMDependenciesFromFrontmatter(frontmatter)
+ require.NotNil(t, result, "Should return non-nil APMDependenciesInfo")
+ assert.Nil(t, result.GitHubApp, "GitHubApp should be nil for array format")
+ })
+}
+
+func TestExtractAPMDependenciesVersion(t *testing.T) {
+ t.Run("Object format with version field", func(t *testing.T) {
+ frontmatter := map[string]any{
+ "dependencies": map[string]any{
+ "packages": []any{"microsoft/apm-sample-package"},
+ "version": "v1.0.0",
+ },
+ }
+ result := extractAPMDependenciesFromFrontmatter(frontmatter)
+ require.NotNil(t, result, "Should return non-nil APMDependenciesInfo")
+ assert.Equal(t, "v1.0.0", result.Version, "Version should be extracted from object format")
+ })
+
+ t.Run("Array format has no version field", func(t *testing.T) {
+ frontmatter := map[string]any{
+ "dependencies": []any{"microsoft/apm-sample-package"},
+ }
+ result := extractAPMDependenciesFromFrontmatter(frontmatter)
+ require.NotNil(t, result, "Should return non-nil APMDependenciesInfo")
+ assert.Empty(t, result.Version, "Version should be empty for array format")
+ })
+
+ t.Run("Object format without version uses empty string", func(t *testing.T) {
+ frontmatter := map[string]any{
+ "dependencies": map[string]any{
+ "packages": []any{"microsoft/apm-sample-package"},
+ },
+ }
+ result := extractAPMDependenciesFromFrontmatter(frontmatter)
+ require.NotNil(t, result, "Should return non-nil APMDependenciesInfo")
+ assert.Empty(t, result.Version, "Version should be empty when not specified")
+ })
+}
+
func TestEngineGetAPMTarget(t *testing.T) {
tests := []struct {
name string
@@ -182,6 +287,7 @@ func TestGenerateAPMPackStep(t *testing.T) {
"archive: 'true'",
"target: copilot",
"working-directory: /tmp/gh-aw/apm-workspace",
+ "apm-version: ${{ env.GH_AW_INFO_APM_VERSION }}",
},
},
{
@@ -195,6 +301,7 @@ func TestGenerateAPMPackStep(t *testing.T) {
"- microsoft/apm-sample-package",
"- github/skills/review",
"target: claude",
+ "apm-version: ${{ env.GH_AW_INFO_APM_VERSION }}",
},
},
{
@@ -203,6 +310,15 @@ func TestGenerateAPMPackStep(t *testing.T) {
target: "all",
expectedContains: []string{
"target: all",
+ "apm-version: ${{ env.GH_AW_INFO_APM_VERSION }}",
+ },
+ },
+ {
+ name: "Custom APM version still uses env var reference in step",
+ apmDeps: &APMDependenciesInfo{Packages: []string{"microsoft/apm-sample-package"}, Version: "v1.0.0"},
+ target: "copilot",
+ expectedContains: []string{
+ "apm-version: ${{ env.GH_AW_INFO_APM_VERSION }}",
},
},
}
@@ -218,12 +334,7 @@ func TestGenerateAPMPackStep(t *testing.T) {
}
require.NotEmpty(t, step, "Step should not be empty")
-
- var sb strings.Builder
- for _, line := range step {
- sb.WriteString(line + "\n")
- }
- combined := sb.String()
+ combined := combineStepLines(step)
for _, expected := range tt.expectedContains {
assert.Contains(t, combined, expected, "Step should contain: %s", expected)
@@ -257,6 +368,7 @@ func TestGenerateAPMRestoreStep(t *testing.T) {
"Restore APM dependencies",
"microsoft/apm-action",
"bundle: /tmp/gh-aw/apm-bundle/*.tar.gz",
+ "apm-version: ${{ env.GH_AW_INFO_APM_VERSION }}",
},
expectedNotContains: []string{"isolated"},
},
@@ -268,6 +380,14 @@ func TestGenerateAPMRestoreStep(t *testing.T) {
"microsoft/apm-action",
"bundle: /tmp/gh-aw/apm-bundle/*.tar.gz",
"isolated: 'true'",
+ "apm-version: ${{ env.GH_AW_INFO_APM_VERSION }}",
+ },
+ },
+ {
+ name: "Custom APM version still uses env var reference in step",
+ apmDeps: &APMDependenciesInfo{Packages: []string{"microsoft/apm-sample-package"}, Version: "v1.0.0"},
+ expectedContains: []string{
+ "apm-version: ${{ env.GH_AW_INFO_APM_VERSION }}",
},
},
}
@@ -283,12 +403,7 @@ func TestGenerateAPMRestoreStep(t *testing.T) {
}
require.NotEmpty(t, step, "Step should not be empty")
-
- var sb strings.Builder
- for _, line := range step {
- sb.WriteString(line + "\n")
- }
- combined := sb.String()
+ combined := combineStepLines(step)
for _, expected := range tt.expectedContains {
assert.Contains(t, combined, expected, "Step should contain: %s", expected)
@@ -299,3 +414,126 @@ func TestGenerateAPMRestoreStep(t *testing.T) {
})
}
}
+
+func TestGenerateAPMPackStepWithGitHubApp(t *testing.T) {
+ t.Run("Pack step includes GITHUB_TOKEN env when github-app is configured", func(t *testing.T) {
+ apmDeps := &APMDependenciesInfo{
+ Packages: []string{"acme-org/acme-skills/plugins/dev-tools"},
+ GitHubApp: &GitHubAppConfig{
+ AppID: "${{ vars.APP_ID }}",
+ PrivateKey: "${{ secrets.APP_PRIVATE_KEY }}",
+ },
+ }
+ data := &WorkflowData{Name: "test-workflow"}
+ step := GenerateAPMPackStep(apmDeps, "claude", data)
+
+ require.NotEmpty(t, step, "Step should not be empty")
+ combined := combineStepLines(step)
+
+ assert.Contains(t, combined, "GITHUB_TOKEN: ${{ steps.apm-app-token.outputs.token }}", "Should inject app token as GITHUB_TOKEN")
+ assert.Contains(t, combined, "env:", "Should have env section")
+ assert.Contains(t, combined, "- acme-org/acme-skills/plugins/dev-tools", "Should list dependency")
+ })
+
+ t.Run("Pack step has no env section without github-app", func(t *testing.T) {
+ apmDeps := &APMDependenciesInfo{
+ Packages: []string{"microsoft/apm-sample-package"},
+ }
+ data := &WorkflowData{Name: "test-workflow"}
+ step := GenerateAPMPackStep(apmDeps, "copilot", data)
+
+ require.NotEmpty(t, step, "Step should not be empty")
+ combined := combineStepLines(step)
+
+ assert.NotContains(t, combined, "GITHUB_TOKEN:", "Should not have GITHUB_TOKEN without github-app")
+ assert.NotContains(t, combined, "apm-app-token", "Should not reference app token without github-app")
+ })
+}
+
+func TestBuildAPMAppTokenMintStep(t *testing.T) {
+ t.Run("Basic app token mint step", func(t *testing.T) {
+ app := &GitHubAppConfig{
+ AppID: "${{ vars.APP_ID }}",
+ PrivateKey: "${{ secrets.APP_PRIVATE_KEY }}",
+ }
+ steps := buildAPMAppTokenMintStep(app, "")
+
+ combined := strings.Join(steps, "")
+ assert.Contains(t, combined, "Generate GitHub App token for APM dependencies", "Should have descriptive step name")
+ assert.Contains(t, combined, "id: apm-app-token", "Should use apm-app-token step ID")
+ assert.Contains(t, combined, "actions/create-github-app-token", "Should use create-github-app-token action")
+ assert.Contains(t, combined, "app-id: ${{ vars.APP_ID }}", "Should include app-id")
+ assert.Contains(t, combined, "private-key: ${{ secrets.APP_PRIVATE_KEY }}", "Should include private-key")
+ assert.Contains(t, combined, "github-api-url: ${{ github.api_url }}", "Should include github-api-url")
+ })
+
+ t.Run("App token mint step with explicit owner", func(t *testing.T) {
+ app := &GitHubAppConfig{
+ AppID: "${{ vars.APP_ID }}",
+ PrivateKey: "${{ secrets.APP_PRIVATE_KEY }}",
+ Owner: "acme-org",
+ }
+ steps := buildAPMAppTokenMintStep(app, "")
+
+ combined := strings.Join(steps, "")
+ assert.Contains(t, combined, "owner: acme-org", "Should include explicit owner")
+ })
+
+ t.Run("App token mint step defaults to github.repository_owner when owner not set", func(t *testing.T) {
+ app := &GitHubAppConfig{
+ AppID: "${{ vars.APP_ID }}",
+ PrivateKey: "${{ secrets.APP_PRIVATE_KEY }}",
+ }
+ steps := buildAPMAppTokenMintStep(app, "")
+
+ combined := strings.Join(steps, "")
+ assert.Contains(t, combined, "owner: ${{ github.repository_owner }}", "Should default owner to github.repository_owner")
+ })
+
+ t.Run("App token mint step with wildcard repositories omits repositories field", func(t *testing.T) {
+ app := &GitHubAppConfig{
+ AppID: "${{ vars.APP_ID }}",
+ PrivateKey: "${{ secrets.APP_PRIVATE_KEY }}",
+ Repositories: []string{"*"},
+ }
+ steps := buildAPMAppTokenMintStep(app, "")
+
+ combined := strings.Join(steps, "")
+ assert.NotContains(t, combined, "repositories:", "Should omit repositories field for org-wide access")
+ })
+
+ t.Run("App token mint step with explicit repositories", func(t *testing.T) {
+ app := &GitHubAppConfig{
+ AppID: "${{ vars.APP_ID }}",
+ PrivateKey: "${{ secrets.APP_PRIVATE_KEY }}",
+ Repositories: []string{"acme-skills"},
+ }
+ steps := buildAPMAppTokenMintStep(app, "")
+
+ combined := strings.Join(steps, "")
+ assert.Contains(t, combined, "repositories: acme-skills", "Should include explicit repository")
+ })
+
+ t.Run("App token mint step uses fallbackRepoExpr when no repositories configured", func(t *testing.T) {
+ app := &GitHubAppConfig{
+ AppID: "${{ vars.APP_ID }}",
+ PrivateKey: "${{ secrets.APP_PRIVATE_KEY }}",
+ }
+ steps := buildAPMAppTokenMintStep(app, "${{ steps.resolve-host-repo.outputs.target_repo_name }}")
+
+ combined := strings.Join(steps, "")
+ assert.Contains(t, combined, "repositories: ${{ steps.resolve-host-repo.outputs.target_repo_name }}", "Should use fallback repo expr for workflow_call relay")
+ assert.NotContains(t, combined, "github.event.repository.name", "Should not fall back to event repository")
+ })
+
+ t.Run("App token mint step defaults to github.event.repository.name when no fallback and no repositories", func(t *testing.T) {
+ app := &GitHubAppConfig{
+ AppID: "${{ vars.APP_ID }}",
+ PrivateKey: "${{ secrets.APP_PRIVATE_KEY }}",
+ }
+ steps := buildAPMAppTokenMintStep(app, "")
+
+ combined := strings.Join(steps, "")
+ assert.Contains(t, combined, "repositories: ${{ github.event.repository.name }}", "Should default to event repository name")
+ })
+}
diff --git a/pkg/workflow/aw_info_tmp_test.go b/pkg/workflow/aw_info_tmp_test.go
index a26f50da6fe..4e5563f7d38 100644
--- a/pkg/workflow/aw_info_tmp_test.go
+++ b/pkg/workflow/aw_info_tmp_test.go
@@ -56,7 +56,7 @@ This workflow tests that aw_info.json is generated in /tmp directory.
lockStr := string(lockContent)
// Test 1: Verify the step uses the generate_aw_info.cjs module
- if !strings.Contains(lockStr, "require('/opt/gh-aw/actions/generate_aw_info.cjs')") {
+ if !strings.Contains(lockStr, "require("+JsRequireGhAw("actions/generate_aw_info.cjs")+")") {
t.Error("Expected step to require generate_aw_info.cjs module")
}
diff --git a/pkg/workflow/aw_info_versions_test.go b/pkg/workflow/aw_info_versions_test.go
index 5f25b082a22..7203ac058fd 100644
--- a/pkg/workflow/aw_info_versions_test.go
+++ b/pkg/workflow/aw_info_versions_test.go
@@ -382,3 +382,64 @@ func TestAllVersionsInAwInfo(t *testing.T) {
t.Errorf("Expected output to contain awmg_version '%s', got:\n%s", expectedAwmgLine, output)
}
}
+
+func TestApmVersionInAwInfo(t *testing.T) {
+ tests := []struct {
+ name string
+ apmDeps *APMDependenciesInfo
+ expectedApmVersion string
+ description string
+ }{
+ {
+ name: "APM deps with explicit version",
+ apmDeps: &APMDependenciesInfo{Packages: []string{"microsoft/apm-sample-package"}, Version: "v1.0.0"},
+ expectedApmVersion: "v1.0.0",
+ description: "Should use explicit APM version when provided",
+ },
+ {
+ name: "APM deps with default version",
+ apmDeps: &APMDependenciesInfo{Packages: []string{"microsoft/apm-sample-package"}},
+ expectedApmVersion: string(constants.DefaultAPMVersion),
+ description: "Should use default APM version when not specified",
+ },
+ {
+ name: "No APM deps configured",
+ apmDeps: nil,
+ expectedApmVersion: "",
+ description: "Should not emit GH_AW_INFO_APM_VERSION when no APM dependencies are configured",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ compiler := NewCompilerWithVersion("1.0.0")
+ registry := GetGlobalEngineRegistry()
+ engine, err := registry.GetEngine("copilot")
+ if err != nil {
+ t.Fatalf("Failed to get copilot engine: %v", err)
+ }
+
+ workflowData := &WorkflowData{
+ Name: "Test Workflow",
+ APMDependencies: tt.apmDeps,
+ }
+
+ var yaml strings.Builder
+ compiler.generateCreateAwInfo(&yaml, workflowData, engine)
+ output := yaml.String()
+
+ if tt.expectedApmVersion == "" {
+ if strings.Contains(output, "GH_AW_INFO_APM_VERSION") {
+ t.Errorf("%s: Expected output to NOT contain GH_AW_INFO_APM_VERSION, got:\n%s",
+ tt.description, output)
+ }
+ } else {
+ expectedLine := `GH_AW_INFO_APM_VERSION: "` + tt.expectedApmVersion + `"`
+ if !strings.Contains(output, expectedLine) {
+ t.Errorf("%s: Expected output to contain '%s', got:\n%s",
+ tt.description, expectedLine, output)
+ }
+ }
+ })
+ }
+}
diff --git a/pkg/workflow/cache.go b/pkg/workflow/cache.go
index 3f6f8dd9b42..ecbe0043026 100644
--- a/pkg/workflow/cache.go
+++ b/pkg/workflow/cache.go
@@ -360,7 +360,7 @@ func generateCacheMemorySteps(builder *strings.Builder, data *WorkflowData) {
if useBackwardCompatiblePaths {
// For single default cache, use the original directory for backward compatibility
builder.WriteString(" - name: Create cache-memory directory\n")
- builder.WriteString(" run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh\n")
+ builder.WriteString(" run: bash " + GhAwHome + "/actions/create_cache_memory_dir.sh\n")
} else {
fmt.Fprintf(builder, " - name: Create cache-memory directory (%s)\n", cache.ID)
builder.WriteString(" run: |\n")
@@ -498,9 +498,9 @@ func generateCacheMemoryValidation(builder *strings.Builder, data *WorkflowData)
// Build validation script
var validationScript strings.Builder
- validationScript.WriteString(" const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');\n")
+ validationScript.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
validationScript.WriteString(" setupGlobals(core, github, context, exec, io);\n")
- validationScript.WriteString(" const { validateMemoryFiles } = require('/opt/gh-aw/actions/validate_memory_files.cjs');\n")
+ validationScript.WriteString(" const { validateMemoryFiles } = require(" + JsRequireGhAw("actions/validate_memory_files.cjs") + ");\n")
fmt.Fprintf(&validationScript, " const allowedExtensions = %s;\n", allowedExtsJSON)
fmt.Fprintf(&validationScript, " const result = validateMemoryFiles('%s', 'cache', allowedExtensions);\n", cacheDir)
validationScript.WriteString(" if (!result.valid) {\n")
@@ -770,9 +770,9 @@ func (c *Compiler) buildUpdateCacheMemoryJob(data *WorkflowData, threatDetection
// Build validation script
var validationScript strings.Builder
- validationScript.WriteString(" const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');\n")
+ validationScript.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
validationScript.WriteString(" setupGlobals(core, github, context, exec, io);\n")
- validationScript.WriteString(" const { validateMemoryFiles } = require('/opt/gh-aw/actions/validate_memory_files.cjs');\n")
+ validationScript.WriteString(" const { validateMemoryFiles } = require(" + JsRequireGhAw("actions/validate_memory_files.cjs") + ");\n")
fmt.Fprintf(&validationScript, " const allowedExtensions = %s;\n", allowedExtsJSON)
fmt.Fprintf(&validationScript, " const result = validateMemoryFiles('%s', 'cache', allowedExtensions);\n", cacheDir)
validationScript.WriteString(" if (!result.valid) {\n")
@@ -844,11 +844,11 @@ func (c *Compiler) buildUpdateCacheMemoryJob(data *WorkflowData, threatDetection
}
// Set GH_AW_WORKFLOW_ID_SANITIZED so cache keys match those used in the agent job
- var jobEnv map[string]string
+ jobEnv := map[string]string{
+ "GH_AW_HOME": GhAwHomeExprDefault,
+ }
if data.WorkflowID != "" {
- jobEnv = map[string]string{
- "GH_AW_WORKFLOW_ID_SANITIZED": SanitizeWorkflowIDForCacheKey(data.WorkflowID),
- }
+ jobEnv["GH_AW_WORKFLOW_ID_SANITIZED"] = SanitizeWorkflowIDForCacheKey(data.WorkflowID)
}
job := &Job{
diff --git a/pkg/workflow/cache_memory_integration_test.go b/pkg/workflow/cache_memory_integration_test.go
index b920405868a..e887853c8ac 100644
--- a/pkg/workflow/cache_memory_integration_test.go
+++ b/pkg/workflow/cache_memory_integration_test.go
@@ -42,7 +42,7 @@ tools:
"uses: actions/cache@",
"key: memory-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}",
"path: /tmp/gh-aw/cache-memory",
- "cat \"/opt/gh-aw/prompts/cache_memory_prompt.md\"",
+ "cat \"" + GhAwHome + "/prompts/cache_memory_prompt.md\"",
"GH_AW_CACHE_DIR: '/tmp/gh-aw/cache-memory/'",
"GH_AW_CACHE_DIR: process.env.GH_AW_CACHE_DIR",
},
diff --git a/pkg/workflow/call_workflow_compilation_test.go b/pkg/workflow/call_workflow_compilation_test.go
index 4e66ef59e49..2323561f581 100644
--- a/pkg/workflow/call_workflow_compilation_test.go
+++ b/pkg/workflow/call_workflow_compilation_test.go
@@ -808,3 +808,105 @@ Worker is in .github/workflows - should be found via directory discovery.
assert.Contains(t, yaml, "call-worker-a:", "Should find and generate fan-out job for worker in workflows dir")
assert.Contains(t, yaml, ".github/workflows/worker-a.lock.yml", "Should reference worker in workflows dir")
}
+
+// TestCallWorkflowCompile_ForwardsTypedInputsAlongsidePayload tests that the compiler
+// emits fromJSON-derived with: entries for each declared workflow_call input (except payload)
+// alongside the canonical payload entry in the generated fan-out job.
+func TestCallWorkflowCompile_ForwardsTypedInputsAlongsidePayload(t *testing.T) {
+ tmpDir := testutil.TempDir(t, "call-workflow-forward-inputs")
+ workflowsDir := filepath.Join(tmpDir, ".github", "workflows")
+ require.NoError(t, os.MkdirAll(workflowsDir, 0755))
+
+ typedInputs := ` payload:
+ type: string
+ required: false
+ source_repo:
+ description: Repository to check out
+ type: string
+ required: false
+ issue_number:
+ description: Issue number to process
+ type: string
+ required: false
+`
+ createWorker(t, workflowsDir, "worker-a", typedInputs)
+
+ gatewayMD := `---
+on: workflow_dispatch
+engine: copilot
+permissions:
+ contents: read
+safe-outputs:
+ add-comment:
+ max: 1
+ call-workflow:
+ workflows:
+ - worker-a
+---
+
+# Gateway — typed input forwarding
+`
+ yaml := compileAndReadLock(t, filepath.Join(workflowsDir, "gateway.md"), gatewayMD)
+
+ // Canonical payload transport must always be present
+ assert.Contains(t, yaml, "payload: ${{ needs.safe_outputs.outputs.call_workflow_payload }}",
+ "Should always forward canonical payload")
+
+ // Typed inputs must be derived from the payload via fromJSON
+ assert.Contains(t, yaml, "fromJSON(needs.safe_outputs.outputs.call_workflow_payload).source_repo",
+ "Should forward source_repo via fromJSON")
+ assert.Contains(t, yaml, "fromJSON(needs.safe_outputs.outputs.call_workflow_payload).issue_number",
+ "Should forward issue_number via fromJSON")
+
+ // payload must not appear as a fromJSON entry (it must remain the raw string)
+ assert.NotContains(t, yaml, "fromJSON(needs.safe_outputs.outputs.call_workflow_payload).payload",
+ "payload itself must not be duplicated as a fromJSON entry")
+}
+
+// TestCallWorkflowCompile_ForwardsHyphenatedInputs tests that the compiler correctly emits
+// fromJSON-derived with: entries for inputs whose names contain hyphens (e.g. "task-description"),
+// which are not valid Go/YAML identifiers but are valid GitHub Actions input names.
+func TestCallWorkflowCompile_ForwardsHyphenatedInputs(t *testing.T) {
+ tmpDir := testutil.TempDir(t, "call-workflow-hyphen-inputs")
+ workflowsDir := filepath.Join(tmpDir, ".github", "workflows")
+ require.NoError(t, os.MkdirAll(workflowsDir, 0755))
+
+ typedInputs := ` payload:
+ type: string
+ required: false
+ task-description:
+ description: Human-readable description of the task to perform
+ type: string
+ required: false
+`
+ createWorker(t, workflowsDir, "worker-a", typedInputs)
+
+ gatewayMD := `---
+on: workflow_dispatch
+engine: copilot
+permissions:
+ contents: read
+safe-outputs:
+ add-comment:
+ max: 1
+ call-workflow:
+ workflows:
+ - worker-a
+---
+
+# Gateway — hyphenated input forwarding
+`
+ yaml := compileAndReadLock(t, filepath.Join(workflowsDir, "gateway.md"), gatewayMD)
+
+ // Canonical payload transport must always be present
+ assert.Contains(t, yaml, "payload: ${{ needs.safe_outputs.outputs.call_workflow_payload }}",
+ "Should always forward canonical payload")
+
+ // Hyphenated input must be forwarded via fromJSON
+ assert.Contains(t, yaml, "fromJSON(needs.safe_outputs.outputs.call_workflow_payload).task-description",
+ "Should forward task-description (hyphenated) via fromJSON")
+
+ // payload must not appear as a fromJSON entry
+ assert.NotContains(t, yaml, "fromJSON(needs.safe_outputs.outputs.call_workflow_payload).payload",
+ "payload itself must not be duplicated as a fromJSON entry")
+}
diff --git a/pkg/workflow/call_workflow_validation.go b/pkg/workflow/call_workflow_validation.go
index 2c73be00baf..fc5b7910825 100644
--- a/pkg/workflow/call_workflow_validation.go
+++ b/pkg/workflow/call_workflow_validation.go
@@ -291,18 +291,5 @@ func mdHasWorkflowCall(mdPath string) (bool, error) {
// - []any: "on: [push, workflow_call]"
// - map[string]any: "on:\n workflow_call: ..."
func containsWorkflowCall(onSection any) bool {
- switch on := onSection.(type) {
- case string:
- return on == "workflow_call"
- case []any:
- for _, trigger := range on {
- if triggerStr, ok := trigger.(string); ok && triggerStr == "workflow_call" {
- return true
- }
- }
- case map[string]any:
- _, ok := on["workflow_call"]
- return ok
- }
- return false
+ return containsTrigger(onSection, "workflow_call")
}
diff --git a/pkg/workflow/cjs_require_validation_test.go b/pkg/workflow/cjs_require_validation_test.go
index b858f748f98..d43816d532b 100644
--- a/pkg/workflow/cjs_require_validation_test.go
+++ b/pkg/workflow/cjs_require_validation_test.go
@@ -15,7 +15,7 @@ import (
// do not use require() statements with "actions/" paths or "@actions/*" npm packages.
//
// When these .cjs files are deployed to GitHub Actions runners, they are copied
-// to /opt/gh-aw/actions/ as a flat directory structure. Any require() statements
+// to ${GH_AW_HOME}/actions/ as a flat directory structure. Any require() statements
// that reference "actions/..." paths or "@actions/*" npm packages would fail because:
// 1. There's no parent "actions/" directory in the runtime environment
// 2. All files are in the same flat directory
@@ -166,7 +166,7 @@ func TestCJSFilesNoActionsRequires(t *testing.T) {
t.Errorf(" - %s", violation)
}
t.Error("\nWhen .cjs files are deployed to GitHub Actions runners, they are copied")
- t.Error("to /opt/gh-aw/actions/ as a flat directory. Any require() statements that")
+ t.Error("to ${GH_AW_HOME}/actions/ as a flat directory. Any require() statements that")
t.Error("reference 'actions/...' paths or '@actions/*' npm packages will fail at runtime")
t.Error("because:")
t.Error(" 1. The parent 'actions/' directory structure doesn't exist")
diff --git a/pkg/workflow/claude_engine.go b/pkg/workflow/claude_engine.go
index 33651bf37f5..1aba19274ed 100644
--- a/pkg/workflow/claude_engine.go
+++ b/pkg/workflow/claude_engine.go
@@ -293,6 +293,10 @@ func (e *ClaudeEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str
// Build the AWF-wrapped command using helper function
// Get allowed domains (Claude defaults + network permissions + HTTP MCP server URLs + runtime ecosystem domains)
allowedDomains := GetClaudeAllowedDomainsWithToolsAndRuntimes(workflowData.NetworkPermissions, workflowData.Tools, workflowData.Runtimes)
+ // Add GHES/custom API target domains to the firewall allow-list when engine.api-target is set
+ if workflowData.EngineConfig != nil && workflowData.EngineConfig.APITarget != "" {
+ allowedDomains = mergeAPITargetDomains(allowedDomains, workflowData.EngineConfig.APITarget)
+ }
// Build AWF command with all configuration
// AWF v0.15.0+ uses chroot mode by default, providing transparent access to host binaries
diff --git a/pkg/workflow/claude_mcp.go b/pkg/workflow/claude_mcp.go
index 111c8185cbf..032c1888e5b 100644
--- a/pkg/workflow/claude_mcp.go
+++ b/pkg/workflow/claude_mcp.go
@@ -12,60 +12,17 @@ var claudeMCPLog = logger.New("workflow:claude_mcp")
func (e *ClaudeEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]any, mcpTools []string, workflowData *WorkflowData) error {
claudeMCPLog.Printf("Rendering MCP config for Claude: tool_count=%d, mcp_tool_count=%d", len(tools), len(mcpTools))
- // Create unified renderer with Claude-specific options
// Claude uses JSON format without Copilot-specific fields and multi-line args
- createRenderer := func(isLast bool) *MCPConfigRendererUnified {
- return NewMCPConfigRenderer(MCPRendererOptions{
- IncludeCopilotFields: false, // Claude doesn't use "type" and "tools" fields
- InlineArgs: false, // Claude uses multi-line args format
- Format: "json",
- IsLast: isLast,
- ActionMode: GetActionModeFromWorkflowData(workflowData),
- WriteSinkGuardPolicies: deriveWriteSinkGuardPolicyFromWorkflow(workflowData),
- })
- }
+ createRenderer := buildMCPRendererFactory(workflowData, "json", false, false)
// Build gateway configuration for MCP config
// Per MCP Gateway Specification v1.0.0 section 4.1.3, the gateway section is required
- gatewayConfig := buildMCPGatewayConfig(workflowData)
-
- // Use shared JSON MCP config renderer with unified renderer methods
return RenderJSONMCPConfig(yaml, tools, mcpTools, workflowData, JSONMCPConfigOptions{
ConfigPath: "/tmp/gh-aw/mcp-config/mcp-servers.json",
- GatewayConfig: gatewayConfig,
- Renderers: MCPToolRenderers{
- RenderGitHub: func(yaml *strings.Builder, githubTool any, isLast bool, workflowData *WorkflowData) {
- renderer := createRenderer(isLast)
- renderer.RenderGitHubMCP(yaml, githubTool, workflowData)
- },
- RenderPlaywright: func(yaml *strings.Builder, playwrightTool any, isLast bool) {
- renderer := createRenderer(isLast)
- renderer.RenderPlaywrightMCP(yaml, playwrightTool)
- },
- RenderSerena: func(yaml *strings.Builder, serenaTool any, isLast bool) {
- renderer := createRenderer(isLast)
- renderer.RenderSerenaMCP(yaml, serenaTool)
- },
- RenderCacheMemory: noOpCacheMemoryRenderer,
- RenderAgenticWorkflows: func(yaml *strings.Builder, isLast bool) {
- renderer := createRenderer(isLast)
- renderer.RenderAgenticWorkflowsMCP(yaml)
- },
- RenderSafeOutputs: func(yaml *strings.Builder, isLast bool, workflowData *WorkflowData) {
- renderer := createRenderer(isLast)
- renderer.RenderSafeOutputsMCP(yaml, workflowData)
- },
- RenderMCPScripts: func(yaml *strings.Builder, mcpScripts *MCPScriptsConfig, isLast bool) {
- renderer := createRenderer(isLast)
- renderer.RenderMCPScriptsMCP(yaml, mcpScripts, workflowData)
- },
- RenderWebFetch: func(yaml *strings.Builder, isLast bool) {
- renderMCPFetchServerConfig(yaml, "json", " ", isLast, false, deriveWriteSinkGuardPolicyFromWorkflow(workflowData))
- },
- RenderCustomMCPConfig: func(yaml *strings.Builder, toolName string, toolConfig map[string]any, isLast bool) error {
- return e.renderClaudeMCPConfigWithContext(yaml, toolName, toolConfig, isLast, workflowData)
- },
- },
+ GatewayConfig: buildMCPGatewayConfig(workflowData),
+ Renderers: buildStandardJSONMCPRenderers(workflowData, createRenderer, false, func(yaml *strings.Builder, toolName string, toolConfig map[string]any, isLast bool) error {
+ return e.renderClaudeMCPConfigWithContext(yaml, toolName, toolConfig, isLast, workflowData)
+ }),
})
}
diff --git a/pkg/workflow/codex_engine.go b/pkg/workflow/codex_engine.go
index 3c546c018da..8cfd960e341 100644
--- a/pkg/workflow/codex_engine.go
+++ b/pkg/workflow/codex_engine.go
@@ -218,6 +218,10 @@ func (e *CodexEngine) GetExecutionSteps(workflowData *WorkflowData, logFile stri
// Build AWF-wrapped command using helper function
// Get allowed domains (Codex defaults + network permissions + HTTP MCP server URLs + runtime ecosystem domains)
allowedDomains := GetCodexAllowedDomainsWithToolsAndRuntimes(workflowData.NetworkPermissions, workflowData.Tools, workflowData.Runtimes)
+ // Add GHES/custom API target domains to the firewall allow-list when engine.api-target is set
+ if workflowData.EngineConfig != nil && workflowData.EngineConfig.APITarget != "" {
+ allowedDomains = mergeAPITargetDomains(allowedDomains, workflowData.EngineConfig.APITarget)
+ }
// Build the command with agent file handling if specified
// INSTRUCTION reading is done inside the AWF command to avoid Docker Compose interpolation
diff --git a/pkg/workflow/codex_engine_test.go b/pkg/workflow/codex_engine_test.go
index a1aa6470302..4480d51fd21 100644
--- a/pkg/workflow/codex_engine_test.go
+++ b/pkg/workflow/codex_engine_test.go
@@ -208,17 +208,22 @@ func TestCodexEngineRenderMCPConfig(t *testing.T) {
"GH_AW_MCP_CONFIG_EOF",
"",
"# Generate JSON config for MCP gateway",
- "cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh",
+ "cat << GH_AW_MCP_CONFIG_EOF | bash " + GhAwHome + "/actions/start_mcp_gateway.sh",
"{",
"\"mcpServers\": {",
"\"github\": {",
fmt.Sprintf("\"container\": \"ghcr.io/github/github-mcp-server:%s\",", constants.DefaultGitHubMCPServerVersion),
"\"env\": {",
"\"GITHUB_HOST\": \"$GITHUB_SERVER_URL\",",
- "\"GITHUB_LOCKDOWN_MODE\": \"$GITHUB_MCP_LOCKDOWN\",",
"\"GITHUB_PERSONAL_ACCESS_TOKEN\": \"$GITHUB_MCP_SERVER_TOKEN\",",
"\"GITHUB_READ_ONLY\": \"1\",",
"\"GITHUB_TOOLSETS\": \"context,repos,issues,pull_requests\"",
+ "},",
+ "\"guard-policies\": {",
+ "\"allow-only\": {",
+ "\"min-integrity\": \"$GITHUB_MCP_GUARD_MIN_INTEGRITY\",",
+ "\"repos\": \"$GITHUB_MCP_GUARD_REPOS\"",
+ "}",
"}",
"}",
"},",
diff --git a/pkg/workflow/codex_mcp.go b/pkg/workflow/codex_mcp.go
index 2e25c1675b0..f7af0ba577a 100644
--- a/pkg/workflow/codex_mcp.go
+++ b/pkg/workflow/codex_mcp.go
@@ -107,59 +107,15 @@ func (e *CodexEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]an
gatewayConfig := buildMCPGatewayConfig(workflowData)
// Use shared JSON renderer for gateway input
- createJSONRenderer := func(isLast bool) *MCPConfigRendererUnified {
- actionMode := ActionModeDev // Default to dev mode
- if workflowData != nil {
- actionMode = workflowData.ActionMode
- }
- return NewMCPConfigRenderer(MCPRendererOptions{
- IncludeCopilotFields: false, // Gateway doesn't need Copilot fields
- InlineArgs: false, // Use standard multi-line format
- Format: "json",
- IsLast: isLast,
- ActionMode: actionMode,
- WriteSinkGuardPolicies: deriveWriteSinkGuardPolicyFromWorkflow(workflowData),
- })
- }
+ // Gateway uses JSON format without Copilot-specific fields and multi-line args
+ createJSONRenderer := buildMCPRendererFactory(workflowData, "json", false, false)
return RenderJSONMCPConfig(yaml, tools, mcpTools, workflowData, JSONMCPConfigOptions{
ConfigPath: "/tmp/gh-aw/mcp-config/mcp-servers.json",
GatewayConfig: gatewayConfig,
- Renderers: MCPToolRenderers{
- RenderGitHub: func(yaml *strings.Builder, githubTool any, isLast bool, workflowData *WorkflowData) {
- renderer := createJSONRenderer(isLast)
- renderer.RenderGitHubMCP(yaml, githubTool, workflowData)
- },
- RenderPlaywright: func(yaml *strings.Builder, playwrightTool any, isLast bool) {
- renderer := createJSONRenderer(isLast)
- renderer.RenderPlaywrightMCP(yaml, playwrightTool)
- },
- RenderSerena: func(yaml *strings.Builder, serenaTool any, isLast bool) {
- renderer := createJSONRenderer(isLast)
- renderer.RenderSerenaMCP(yaml, serenaTool)
- },
- RenderCacheMemory: func(yaml *strings.Builder, isLast bool, workflowData *WorkflowData) {
- // Cache-memory is not used as MCP server
- },
- RenderAgenticWorkflows: func(yaml *strings.Builder, isLast bool) {
- renderer := createJSONRenderer(isLast)
- renderer.RenderAgenticWorkflowsMCP(yaml)
- },
- RenderSafeOutputs: func(yaml *strings.Builder, isLast bool, workflowData *WorkflowData) {
- renderer := createJSONRenderer(isLast)
- renderer.RenderSafeOutputsMCP(yaml, workflowData)
- },
- RenderMCPScripts: func(yaml *strings.Builder, mcpScripts *MCPScriptsConfig, isLast bool) {
- renderer := createJSONRenderer(isLast)
- renderer.RenderMCPScriptsMCP(yaml, mcpScripts, workflowData)
- },
- RenderWebFetch: func(yaml *strings.Builder, isLast bool) {
- renderMCPFetchServerConfig(yaml, "json", " ", isLast, false, deriveWriteSinkGuardPolicyFromWorkflow(workflowData))
- },
- RenderCustomMCPConfig: func(yaml *strings.Builder, toolName string, toolConfig map[string]any, isLast bool) error {
- return e.renderCodexJSONMCPConfigWithContext(yaml, toolName, toolConfig, isLast, workflowData)
- },
- },
+ Renderers: buildStandardJSONMCPRenderers(workflowData, createJSONRenderer, false, func(yaml *strings.Builder, toolName string, toolConfig map[string]any, isLast bool) error {
+ return e.renderCodexJSONMCPConfigWithContext(yaml, toolName, toolConfig, isLast, workflowData)
+ }),
})
}
diff --git a/pkg/workflow/compiler_activation_job.go b/pkg/workflow/compiler_activation_job.go
index 95df0b548c4..7f442c686df 100644
--- a/pkg/workflow/compiler_activation_job.go
+++ b/pkg/workflow/compiler_activation_job.go
@@ -1,6 +1,7 @@
package workflow
import (
+ "encoding/json"
"errors"
"fmt"
"strings"
@@ -114,16 +115,31 @@ func (c *Compiler) buildActivationJob(data *WorkflowData, preActivationJobCreate
checkoutSteps := c.generateCheckoutGitHubFolderForActivation(data)
steps = append(steps, checkoutSteps...)
- // Mint a single activation app token upfront if a GitHub App is configured and either
- // the reaction or status-comment step will need it. This avoids minting multiple tokens.
+ // Mint a single activation app token upfront if a GitHub App is configured and any
+ // step in the activation job will need it (reaction, status-comment, or label removal).
+ // This avoids minting multiple tokens.
hasReaction := data.AIReaction != "" && data.AIReaction != "none"
hasStatusComment := data.StatusComment != nil && *data.StatusComment
- if data.ActivationGitHubApp != nil && (hasReaction || hasStatusComment) {
- // Build the combined permissions needed for reactions and/or status comments
+ hasLabelCommand := len(data.LabelCommand) > 0
+ // Compute filtered label events once and reuse below (permissions + app token scopes)
+ filteredLabelEvents := FilterLabelCommandEvents(data.LabelCommandEvents)
+ if data.ActivationGitHubApp != nil && (hasReaction || hasStatusComment || hasLabelCommand) {
+ // Build the combined permissions needed for all activation steps.
+ // For label removal we only add the scopes required by the enabled events.
appPerms := NewPermissions()
- appPerms.Set(PermissionIssues, PermissionWrite)
- appPerms.Set(PermissionPullRequests, PermissionWrite)
- appPerms.Set(PermissionDiscussions, PermissionWrite)
+ if hasReaction || hasStatusComment {
+ appPerms.Set(PermissionIssues, PermissionWrite)
+ appPerms.Set(PermissionPullRequests, PermissionWrite)
+ appPerms.Set(PermissionDiscussions, PermissionWrite)
+ }
+ if hasLabelCommand {
+ if sliceutil.Contains(filteredLabelEvents, "issues") || sliceutil.Contains(filteredLabelEvents, "pull_request") {
+ appPerms.Set(PermissionIssues, PermissionWrite)
+ }
+ if sliceutil.Contains(filteredLabelEvents, "discussion") {
+ appPerms.Set(PermissionDiscussions, PermissionWrite)
+ }
+ }
steps = append(steps, c.buildActivationAppTokenMintStep(data.ActivationGitHubApp, appPerms)...)
}
@@ -284,6 +300,35 @@ func (c *Compiler) buildActivationJob(data *WorkflowData, preActivationJobCreate
}
}
+ // Add label removal step and label_command output for label-command workflows.
+ // When a label-command trigger fires, the triggering label is immediately removed
+ // so that the same label can be applied again to trigger the workflow in the future.
+ if len(data.LabelCommand) > 0 {
+ // The removal step only makes sense for actual "labeled" events; for
+ // workflow_dispatch we skip it silently via the env-based label check.
+ steps = append(steps, " - name: Remove trigger label\n")
+ steps = append(steps, fmt.Sprintf(" id: %s\n", constants.RemoveTriggerLabelStepID))
+ steps = append(steps, fmt.Sprintf(" uses: %s\n", GetActionPin("actions/github-script")))
+ steps = append(steps, " env:\n")
+ // Pass label names as a JSON array so the script can validate the label
+ labelNamesJSON, err := json.Marshal(data.LabelCommand)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal label-command names: %w", err)
+ }
+ steps = append(steps, fmt.Sprintf(" GH_AW_LABEL_NAMES: '%s'\n", string(labelNamesJSON)))
+ steps = append(steps, " with:\n")
+ // Use GitHub App or custom token if configured (avoids needing elevated GITHUB_TOKEN permissions)
+ labelToken := c.resolveActivationToken(data)
+ if labelToken != "${{ secrets.GITHUB_TOKEN }}" {
+ steps = append(steps, fmt.Sprintf(" github-token: %s\n", labelToken))
+ }
+ steps = append(steps, " script: |\n")
+ steps = append(steps, generateGitHubScriptWithRequire("remove_trigger_label.cjs"))
+
+ // Expose the matched label name as a job output for downstream jobs to consume
+ outputs["label_command"] = fmt.Sprintf("${{ steps.%s.outputs.label_name }}", constants.RemoveTriggerLabelStepID)
+ }
+
// If no steps have been added, add a placeholder step to make the job valid
// This can happen when the activation job is created only for an if condition
if len(steps) == 0 {
@@ -364,6 +409,20 @@ func (c *Compiler) buildActivationJob(data *WorkflowData, preActivationJobCreate
// Generate APM pack step if dependencies are specified.
// The pack step runs after prompt generation and uploads as a separate "apm" artifact.
if data.APMDependencies != nil && len(data.APMDependencies.Packages) > 0 {
+ // Mint a GitHub App token before the pack step if a github-app is configured for APM.
+ // This allows APM to access cross-org private repositories.
+ if data.APMDependencies.GitHubApp != nil {
+ compilerActivationJobLog.Print("Adding APM GitHub App token mint step for cross-org access")
+ // In workflow_call relay workflows the activation job contains a resolve-host-repo step
+ // that identifies the platform (host) repository. Use its output as the fallback
+ // repositories value so the minted token is scoped to the host repo's NAME rather than
+ // github.event.repository.name (the caller repo in cross-repo workflow_call scenarios).
+ var apmFallbackRepoExpr string
+ if hasWorkflowCallTrigger(data.On) && !data.InlinedImports {
+ apmFallbackRepoExpr = "${{ steps.resolve-host-repo.outputs.target_repo_name }}"
+ }
+ steps = append(steps, buildAPMAppTokenMintStep(data.APMDependencies.GitHubApp, apmFallbackRepoExpr)...)
+ }
compilerActivationJobLog.Printf("Adding APM pack step: %d packages", len(data.APMDependencies.Packages))
apmTarget := engine.GetAPMTarget()
apmPackStep := GenerateAPMPackStep(data.APMDependencies, apmTarget, data)
@@ -426,6 +485,21 @@ func (c *Compiler) buildActivationJob(data *WorkflowData, preActivationJobCreate
permsMap[PermissionIssues] = PermissionWrite
}
+ // Add write permissions for label removal when label_command is configured.
+ // Only grant the scopes required by the enabled events:
+ // - issues/pull_request events need issues:write (PR labels use the issues REST API)
+ // - discussion events need discussions:write
+ // When a github-app token is configured, the GITHUB_TOKEN permissions are irrelevant
+ // for the label removal step (it uses the app token instead), so we skip them.
+ if hasLabelCommand && data.ActivationGitHubApp == nil {
+ if sliceutil.Contains(filteredLabelEvents, "issues") || sliceutil.Contains(filteredLabelEvents, "pull_request") {
+ permsMap[PermissionIssues] = PermissionWrite
+ }
+ if sliceutil.Contains(filteredLabelEvents, "discussion") {
+ permsMap[PermissionDiscussions] = PermissionWrite
+ }
+ }
+
perms := NewPermissionsFromMap(permsMap)
permissions := perms.RenderToYAML()
@@ -437,6 +511,19 @@ func (c *Compiler) buildActivationJob(data *WorkflowData, preActivationJobCreate
environment = "environment: " + cleanManualApproval
}
+ // Set job-level GH_AW_INFO_APM_VERSION so the apm_pack step can reference it
+ // via ${{ env.GH_AW_INFO_APM_VERSION }} in its with: block
+ var activationJobEnv map[string]string
+ if data.APMDependencies != nil && len(data.APMDependencies.Packages) > 0 {
+ apmVersion := data.APMDependencies.Version
+ if apmVersion == "" {
+ apmVersion = string(constants.DefaultAPMVersion)
+ }
+ activationJobEnv = map[string]string{
+ "GH_AW_INFO_APM_VERSION": apmVersion,
+ }
+ }
+
job := &Job{
Name: string(constants.ActivationJobName),
If: activationCondition,
@@ -444,6 +531,7 @@ func (c *Compiler) buildActivationJob(data *WorkflowData, preActivationJobCreate
RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs),
Permissions: permissions,
Environment: environment,
+ Env: activationJobEnv,
Steps: steps,
Outputs: outputs,
Needs: activationNeeds, // Depend on pre-activation job if it exists
diff --git a/pkg/workflow/compiler_custom_actions_test.go b/pkg/workflow/compiler_custom_actions_test.go
index e2de221cd84..f153c69edcb 100644
--- a/pkg/workflow/compiler_custom_actions_test.go
+++ b/pkg/workflow/compiler_custom_actions_test.go
@@ -247,7 +247,7 @@ Test workflow with script mode.
}
// 5. Setup step should have INPUT_DESTINATION environment variable
- if !strings.Contains(lockStr, "INPUT_DESTINATION: /opt/gh-aw/actions") {
+ if !strings.Contains(lockStr, "INPUT_DESTINATION: "+SetupActionDestination) {
t.Error("Expected INPUT_DESTINATION environment variable in setup step for script mode")
}
diff --git a/pkg/workflow/compiler_jobs.go b/pkg/workflow/compiler_jobs.go
index 9756e09c833..a2c7e95f119 100644
--- a/pkg/workflow/compiler_jobs.go
+++ b/pkg/workflow/compiler_jobs.go
@@ -243,10 +243,11 @@ func (c *Compiler) buildPreActivationAndActivationJobs(data *WorkflowData, front
hasSkipBots := len(data.SkipBots) > 0
hasCommandTrigger := len(data.Command) > 0
hasRateLimit := data.RateLimit != nil
- compilerJobsLog.Printf("Job configuration: needsPermissionCheck=%v, hasStopTime=%v, hasSkipIfMatch=%v, hasSkipIfNoMatch=%v, hasSkipRoles=%v, hasSkipBots=%v, hasCommand=%v, hasRateLimit=%v", needsPermissionCheck, hasStopTime, hasSkipIfMatch, hasSkipIfNoMatch, hasSkipRoles, hasSkipBots, hasCommandTrigger, hasRateLimit)
+ hasOnSteps := len(data.OnSteps) > 0
+ compilerJobsLog.Printf("Job configuration: needsPermissionCheck=%v, hasStopTime=%v, hasSkipIfMatch=%v, hasSkipIfNoMatch=%v, hasSkipRoles=%v, hasSkipBots=%v, hasCommand=%v, hasRateLimit=%v, hasOnSteps=%v", needsPermissionCheck, hasStopTime, hasSkipIfMatch, hasSkipIfNoMatch, hasSkipRoles, hasSkipBots, hasCommandTrigger, hasRateLimit, hasOnSteps)
- // Build pre-activation job if needed (combines membership checks, stop-time validation, skip-if-match check, skip-if-no-match check, skip-roles check, skip-bots check, rate limit check, and command position check)
- if needsPermissionCheck || hasStopTime || hasSkipIfMatch || hasSkipIfNoMatch || hasSkipRoles || hasSkipBots || hasCommandTrigger || hasRateLimit {
+ // Build pre-activation job if needed (combines membership checks, stop-time validation, skip-if-match check, skip-if-no-match check, skip-roles check, skip-bots check, rate limit check, command position check, and on.steps injection)
+ if needsPermissionCheck || hasStopTime || hasSkipIfMatch || hasSkipIfNoMatch || hasSkipRoles || hasSkipBots || hasCommandTrigger || hasRateLimit || hasOnSteps {
compilerJobsLog.Print("Building pre-activation job")
preActivationJob, err := c.buildPreActivationJob(data, needsPermissionCheck)
if err != nil {
diff --git a/pkg/workflow/compiler_main_job.go b/pkg/workflow/compiler_main_job.go
index 11d72efa9a5..5917e0e024a 100644
--- a/pkg/workflow/compiler_main_job.go
+++ b/pkg/workflow/compiler_main_job.go
@@ -174,24 +174,23 @@ func (c *Compiler) buildMainJob(data *WorkflowData, activationJobCreated bool) (
}
}
- // Build job-level environment variables for safe outputs
- var env map[string]string
- if data.SafeOutputs != nil {
- env = make(map[string]string)
+ // Build job-level environment variables
+ // Use GhAwHomeExprDefault so callers can override GH_AW_HOME via workflow/repo env
+ env := map[string]string{
+ "GH_AW_HOME": GhAwHomeExprDefault,
+ }
- // Set GH_AW_SAFE_OUTPUTS to path in /opt (read-only mount for agent container)
- // The MCP server writes agent outputs to this file during execution
- // This file is in /opt to prevent the agent container from having write access
- env["GH_AW_SAFE_OUTPUTS"] = "/opt/gh-aw/safeoutputs/outputs.jsonl"
+ if data.SafeOutputs != nil {
+ // Set GH_AW_SAFE_OUTPUTS and related paths as job-level env vars.
+ // Using GhAwHomeExpr so paths adapt when GH_AW_HOME is overridden.
+ env["GH_AW_SAFE_OUTPUTS"] = GhAwHomeExpr + "/safeoutputs/outputs.jsonl"
+ env["GH_AW_SAFE_OUTPUTS_CONFIG_PATH"] = GhAwHomeExpr + "/safeoutputs/config.json"
+ env["GH_AW_SAFE_OUTPUTS_TOOLS_PATH"] = GhAwHomeExpr + "/safeoutputs/tools.json"
// Set GH_AW_MCP_LOG_DIR for safe outputs MCP server logging
// Store in mcp-logs directory so it's included in mcp-logs artifact
env["GH_AW_MCP_LOG_DIR"] = "/tmp/gh-aw/mcp-logs/safeoutputs"
- // Set config and tools paths (readonly files in /opt/gh-aw)
- env["GH_AW_SAFE_OUTPUTS_CONFIG_PATH"] = "/opt/gh-aw/safeoutputs/config.json"
- env["GH_AW_SAFE_OUTPUTS_TOOLS_PATH"] = "/opt/gh-aw/safeoutputs/tools.json"
-
// Add asset-related environment variables
// These must always be set (even to empty) because awmg v0.0.12+ validates ${VAR} references
if data.SafeOutputs.UploadAssets != nil {
@@ -214,11 +213,21 @@ func (c *Compiler) buildMainJob(data *WorkflowData, activationJobCreated bool) (
// This contains the workflow ID with all hyphens removed and lowercased
// Used in cache keys to avoid spaces and special characters
if data.WorkflowID != "" {
+ sanitizedID := SanitizeWorkflowIDForCacheKey(data.WorkflowID)
+ env["GH_AW_WORKFLOW_ID_SANITIZED"] = sanitizedID
+ }
+
+ // Set job-level GH_AW_INFO_APM_VERSION so the apm_restore step can reference it
+ // via ${{ env.GH_AW_INFO_APM_VERSION }} in its with: block
+ if data.APMDependencies != nil && len(data.APMDependencies.Packages) > 0 {
if env == nil {
env = make(map[string]string)
}
- sanitizedID := SanitizeWorkflowIDForCacheKey(data.WorkflowID)
- env["GH_AW_WORKFLOW_ID_SANITIZED"] = sanitizedID
+ apmVersion := data.APMDependencies.Version
+ if apmVersion == "" {
+ apmVersion = string(constants.DefaultAPMVersion)
+ }
+ env["GH_AW_INFO_APM_VERSION"] = apmVersion
}
// Generate agent concurrency configuration
diff --git a/pkg/workflow/compiler_orchestrator_workflow.go b/pkg/workflow/compiler_orchestrator_workflow.go
index 23ac893f857..90fd4d35641 100644
--- a/pkg/workflow/compiler_orchestrator_workflow.go
+++ b/pkg/workflow/compiler_orchestrator_workflow.go
@@ -596,6 +596,7 @@ func (c *Compiler) extractAdditionalConfigurations(
// Extract and process mcp-scripts and safe-outputs
workflowData.Command, workflowData.CommandEvents = c.extractCommandConfig(frontmatter)
+ workflowData.LabelCommand, workflowData.LabelCommandEvents = c.extractLabelCommandConfig(frontmatter)
workflowData.Jobs = c.extractJobsFromFrontmatter(frontmatter)
// Merge jobs from imported YAML workflows
@@ -727,5 +728,15 @@ func (c *Compiler) processOnSectionAndFilters(
// Apply label filter if specified
c.applyLabelFilter(workflowData, frontmatter)
+ // Extract on.steps for pre-activation step injection
+ onSteps, err := extractOnSteps(frontmatter)
+ if err != nil {
+ return err
+ }
+ workflowData.OnSteps = onSteps
+
+ // Extract on.permissions for pre-activation job permissions
+ workflowData.OnPermissions = extractOnPermissions(frontmatter)
+
return nil
}
diff --git a/pkg/workflow/compiler_pre_activation_job.go b/pkg/workflow/compiler_pre_activation_job.go
index 8ebdbddcb6b..a049bd25927 100644
--- a/pkg/workflow/compiler_pre_activation_job.go
+++ b/pkg/workflow/compiler_pre_activation_job.go
@@ -55,6 +55,15 @@ func (c *Compiler) buildPreActivationJob(data *WorkflowData, needsPermissionChec
perms.Set(PermissionActions, PermissionRead)
}
+ // Merge on.permissions into the pre-activation job permissions.
+ // on.permissions lets users declare extra scopes required by their on.steps steps.
+ if data.OnPermissions != nil {
+ if perms == nil {
+ perms = NewPermissions()
+ }
+ perms.Merge(data.OnPermissions)
+ }
+
// Set permissions if any were configured
if perms != nil {
permissions = perms.RenderToYAML()
@@ -195,6 +204,23 @@ func (c *Compiler) buildPreActivationJob(data *WorkflowData, needsPermissionChec
steps = append(steps, customSteps...)
}
+ // Append on.steps if present (injected after other checks)
+ var onStepIDs []string
+ if len(data.OnSteps) > 0 {
+ compilerActivationJobsLog.Printf("Adding %d on.steps to pre-activation job", len(data.OnSteps))
+ for i, stepMap := range data.OnSteps {
+ stepYAML, err := c.convertStepToYAML(stepMap)
+ if err != nil {
+ return nil, fmt.Errorf("failed to convert on.steps[%d] to YAML: %w", i, err)
+ }
+ steps = append(steps, stepYAML)
+ // Collect step IDs for output wiring
+ if id, ok := stepMap["id"].(string); ok && id != "" {
+ onStepIDs = append(onStepIDs, id)
+ }
+ }
+ }
+
// Generate the activated output expression using expression builders
var activatedNode ConditionNode
@@ -283,9 +309,18 @@ func (c *Compiler) buildPreActivationJob(data *WorkflowData, needsPermissionChec
// Build the final expression
if len(conditions) == 0 {
- // This should never happen - it means pre-activation job was created without any checks
- // If we reach this point, it's a developer error in the compiler logic
- return nil, errors.New("developer error: pre-activation job created without permission check or stop-time configuration")
+ // Pre-activation was created solely for on.steps injection.
+ // The activated output is unconditionally true; the user controls
+ // agent execution through their own if: condition referencing the
+ // on.steps outputs (e.g., needs.pre_activation.outputs.gate_result).
+ if len(data.OnSteps) > 0 {
+ compilerActivationJobsLog.Printf("Pre-activation created with on.steps only (%d steps); activated output is unconditionally true", len(data.OnSteps))
+ activatedNode = BuildStringLiteral("true")
+ } else {
+ // This should never happen - it means pre-activation job was created without any checks
+ // If we reach this point, it's a developer error in the compiler logic
+ return nil, errors.New("developer error: pre-activation job created without permission check or stop-time configuration")
+ }
} else if len(conditions) == 1 {
// Single condition
activatedNode = conditions[0]
@@ -313,7 +348,21 @@ func (c *Compiler) buildPreActivationJob(data *WorkflowData, needsPermissionChec
outputs[constants.MatchedCommandOutput] = "''"
}
- // Merge custom outputs from jobs.pre-activation if present
+ // Wire on.steps step outcomes as pre-activation outputs.
+ // For each step with an id, emit output "_result: ${{ steps..outcome }}"
+ // so users can reference them with: needs.pre_activation.outputs._result
+ // This is done BEFORE merging custom outputs so that explicit user-defined outputs
+ // in jobs.pre-activation.outputs take precedence over the auto-wired values.
+ if len(onStepIDs) > 0 {
+ compilerActivationJobsLog.Printf("Wiring %d on.steps step outcomes as pre-activation outputs", len(onStepIDs))
+ for _, id := range onStepIDs {
+ outputKey := id + "_result"
+ outputs[outputKey] = fmt.Sprintf("${{ steps.%s.outcome }}", id)
+ }
+ }
+
+ // Merge custom outputs from jobs.pre-activation if present.
+ // Custom outputs are applied last so they take precedence over auto-wired on.steps outputs.
if len(customOutputs) > 0 {
compilerActivationJobsLog.Printf("Adding %d custom outputs to pre-activation job", len(customOutputs))
maps.Copy(outputs, customOutputs)
@@ -322,8 +371,10 @@ func (c *Compiler) buildPreActivationJob(data *WorkflowData, needsPermissionChec
// Pre-activation job uses the user's original if condition (data.If)
// The workflow_run safety check is NOT applied here - it's only on the activation job
// Don't include conditions that reference custom job outputs (those belong on the agent job)
+ // Also don't include conditions that reference pre_activation outputs - those are outputs of this
+ // very job and can only be evaluated by downstream jobs (activation, agent).
var jobIfCondition string
- if !c.referencesCustomJobOutputs(data.If, data.Jobs) {
+ if !c.referencesCustomJobOutputs(data.If, data.Jobs) && !referencesPreActivationOutputs(data.If) {
jobIfCondition = data.If
}
@@ -478,3 +529,80 @@ func (c *Compiler) resolvePreActivationSkipIfToken(data *WorkflowData) string {
}
return ""
}
+
+// extractOnSteps extracts the 'steps' field from the 'on:' section of frontmatter.
+// These steps are injected into the pre-activation job and their step outcome is wired
+// as pre-activation outputs so users can reference them with:
+//
+// needs.pre_activation.outputs._result (contains outcome: success/failure/cancelled/skipped)
+//
+// Returns nil if on.steps is not configured.
+// Returns an error if on.steps is not an array or contains non-object items.
+func extractOnSteps(frontmatter map[string]any) ([]map[string]any, error) {
+ onValue, exists := frontmatter["on"]
+ if !exists || onValue == nil {
+ return nil, nil
+ }
+
+ onMap, ok := onValue.(map[string]any)
+ if !ok {
+ return nil, nil
+ }
+
+ stepsValue, exists := onMap["steps"]
+ if !exists || stepsValue == nil {
+ return nil, nil
+ }
+
+ stepsList, ok := stepsValue.([]any)
+ if !ok {
+ return nil, fmt.Errorf("on.steps must be an array, got %T", stepsValue)
+ }
+
+ result := make([]map[string]any, 0, len(stepsList))
+ for i, step := range stepsList {
+ stepMap, ok := step.(map[string]any)
+ if !ok {
+ return nil, fmt.Errorf("on.steps[%d] must be an object, got %T", i, step)
+ }
+ result = append(result, stepMap)
+ }
+
+ return result, nil
+}
+
+// extractOnPermissions extracts the 'permissions' field from the 'on:' section of frontmatter.
+// These permissions are merged into the pre-activation job permissions, allowing users to declare
+// extra scopes required by their on.steps (e.g., issues: read for GitHub API calls).
+//
+// Returns nil if on.permissions is not configured.
+func extractOnPermissions(frontmatter map[string]any) *Permissions {
+ onValue, exists := frontmatter["on"]
+ if !exists || onValue == nil {
+ return nil
+ }
+
+ onMap, ok := onValue.(map[string]any)
+ if !ok {
+ return nil
+ }
+
+ permsValue, exists := onMap["permissions"]
+ if !exists || permsValue == nil {
+ return nil
+ }
+
+ parser := NewPermissionsParserFromValue(permsValue)
+ return parser.ToPermissions()
+}
+
+// referencesPreActivationOutputs returns true if the condition references the pre_activation job's
+// own outputs (e.g., "needs.pre_activation.outputs.foo"). Such conditions cannot be applied to the
+// pre_activation job itself (a job cannot reference its own outputs), so they are deferred to
+// downstream jobs (activation, agent).
+func referencesPreActivationOutputs(condition string) bool {
+ if condition == "" {
+ return false
+ }
+ return strings.Contains(condition, "needs."+string(constants.PreActivationJobName)+".outputs.")
+}
diff --git a/pkg/workflow/compiler_safe_output_jobs.go b/pkg/workflow/compiler_safe_output_jobs.go
index 6d4f1cb9c3e..053bb10a534 100644
--- a/pkg/workflow/compiler_safe_output_jobs.go
+++ b/pkg/workflow/compiler_safe_output_jobs.go
@@ -130,7 +130,10 @@ func (c *Compiler) buildSafeOutputsJobs(data *WorkflowData, jobName, markdownPat
// - depends on safe_outputs
// - has an `if:` that checks needs.safe_outputs.outputs.call_workflow_name
// - uses: the relative path to the worker's .lock.yml (or .yml)
-// - passes payload as a `with:` input
+// - passes payload as the canonical `with:` input
+// - also passes one `with:` entry per declared workflow_call input (except
+// payload) as `fromJSON(needs.safe_outputs.outputs.call_workflow_payload).`
+// so that worker steps can reference inputs. directly
// - inherits all caller secrets via `secrets: inherit`
// - includes a job-level `permissions:` block that is the union of all the
// worker's job-level permissions, so GitHub allows the nested jobs to run
@@ -164,15 +167,57 @@ func (c *Compiler) buildCallWorkflowJobs(data *WorkflowData, markdownPath string
workflowPath = fmt.Sprintf("./.github/workflows/%s.lock.yml", workflowName)
}
+ // Build the with: block. Always include the canonical payload transport,
+ // then add per-input entries derived from the payload for every declared
+ // workflow_call input on the worker (except 'payload' itself) so that
+ // worker steps can reference inputs. directly without parsing JSON.
+ with := map[string]any{
+ "payload": "${{ needs.safe_outputs.outputs.call_workflow_payload }}",
+ }
+
+ if markdownPath != "" {
+ fileResult, findErr := findWorkflowFile(workflowName, markdownPath)
+ if findErr != nil {
+ compilerSafeOutputJobsLog.Printf("Warning: could not find worker workflow file for '%s': %v. "+
+ "Typed inputs will not be forwarded in the with: block.", workflowName, findErr)
+ } else {
+ var workflowInputs map[string]any
+ var inputErr error
+ switch {
+ case fileResult.lockExists:
+ workflowInputs, inputErr = extractWorkflowCallInputs(fileResult.lockPath)
+ case fileResult.ymlExists:
+ workflowInputs, inputErr = extractWorkflowCallInputs(fileResult.ymlPath)
+ case fileResult.mdExists:
+ workflowInputs, inputErr = extractMDWorkflowCallInputs(fileResult.mdPath)
+ default:
+ compilerSafeOutputJobsLog.Printf("Warning: no worker file found for '%s'; "+
+ "typed inputs will not be forwarded in the with: block.", workflowName)
+ }
+ if inputErr != nil {
+ compilerSafeOutputJobsLog.Printf("Warning: could not extract workflow_call inputs for '%s': %v. "+
+ "Typed inputs will not be forwarded in the with: block.", workflowName, inputErr)
+ } else if workflowInputs != nil {
+ typedInputCount := 0
+ for inputName := range workflowInputs {
+ if inputName == "payload" {
+ continue
+ }
+ with[inputName] = fmt.Sprintf("${{ fromJSON(needs.safe_outputs.outputs.call_workflow_payload).%s }}", inputName)
+ typedInputCount++
+ }
+ compilerSafeOutputJobsLog.Printf("Forwarding %d typed inputs for call-workflow job '%s'", typedInputCount, jobName)
+ }
+ }
+ }
+
callJob := &Job{
Name: jobName,
Needs: []string{"safe_outputs"},
If: fmt.Sprintf("needs.safe_outputs.outputs.call_workflow_name == '%s'", workflowName),
Uses: workflowPath,
SecretsInherit: true,
- With: map[string]any{
- "payload": "${{ needs.safe_outputs.outputs.call_workflow_payload }}",
- },
+ With: with,
}
// Compute the permission superset required by the worker's jobs and
diff --git a/pkg/workflow/compiler_safe_outputs.go b/pkg/workflow/compiler_safe_outputs.go
index eea5c8502d7..750cd516763 100644
--- a/pkg/workflow/compiler_safe_outputs.go
+++ b/pkg/workflow/compiler_safe_outputs.go
@@ -22,6 +22,7 @@ func (c *Compiler) parseOnSection(frontmatter map[string]any, workflowData *Work
// Check if "slash_command" or "command" (deprecated) is used as a trigger in the "on" section
// Also extract "reaction" from the "on" section
var hasCommand bool
+ var hasLabelCommand bool
var hasReaction bool
var hasStopAfter bool
var hasStatusComment bool
@@ -139,8 +140,36 @@ func (c *Compiler) parseOnSection(frontmatter map[string]any, workflowData *Work
// Clear the On field so applyDefaults will handle command trigger generation
workflowData.On = ""
}
- // Extract other (non-conflicting) events excluding slash_command, command, reaction, status-comment, and stop-after
- otherEvents = filterMapKeys(onMap, "slash_command", "command", "reaction", "status-comment", "stop-after", "github-token", "github-app")
+
+ // Detect label_command trigger
+ if _, hasLabelCommandKey := onMap["label_command"]; hasLabelCommandKey {
+ hasLabelCommand = true
+ // Set default label names from WorkflowData if already populated by extractLabelCommandConfig
+ if len(workflowData.LabelCommand) == 0 {
+ // extractLabelCommandConfig has not been called yet or returned nothing;
+ // set a placeholder so applyDefaults knows this is a label-command workflow.
+ // The actual label names will be extracted from the frontmatter in applyDefaults
+ // via extractLabelCommandConfig which was called in parseOnSectionRaw.
+ baseName := strings.TrimSuffix(filepath.Base(markdownPath), ".md")
+ workflowData.LabelCommand = []string{baseName}
+ }
+ // Validate: existing issues/pull_request/discussion triggers that have non-label types
+ // would be silently overridden by the label_command generation. Require label-only types
+ // (labeled/unlabeled) so the merge is deterministic and user config is not lost.
+ labelConflictingEvents := []string{"issues", "pull_request", "discussion"}
+ for _, eventName := range labelConflictingEvents {
+ if eventValue, hasConflict := onMap[eventName]; hasConflict {
+ if !parser.IsLabelOnlyEvent(eventValue) {
+ return fmt.Errorf("cannot use 'label_command' with '%s' trigger (non-label types); use only labeled/unlabeled types or remove this trigger", eventName)
+ }
+ }
+ }
+ // Clear the On field so applyDefaults will handle label-command trigger generation
+ workflowData.On = ""
+ }
+
+ // Extract other (non-conflicting) events excluding slash_command, command, label_command, reaction, status-comment, and stop-after
+ otherEvents = filterMapKeys(onMap, "slash_command", "command", "label_command", "reaction", "status-comment", "stop-after", "github-token", "github-app")
}
}
@@ -149,16 +178,32 @@ func (c *Compiler) parseOnSection(frontmatter map[string]any, workflowData *Work
workflowData.Command = nil
}
- // Auto-enable "eyes" reaction for command triggers if no explicit reaction was specified
- if hasCommand && !hasReaction && workflowData.AIReaction == "" {
+ // Clear label-command field if no label_command trigger was found
+ if !hasLabelCommand {
+ workflowData.LabelCommand = nil
+ workflowData.LabelCommandEvents = nil
+ }
+
+ // Auto-enable "eyes" reaction for slash_command/label_command (and deprecated command) triggers if no explicit reaction was specified
+ if (hasCommand || hasLabelCommand) && !hasReaction && workflowData.AIReaction == "" {
workflowData.AIReaction = "eyes"
}
+ // Auto-enable status-comment for slash_command/label_command (and deprecated command) triggers if not explicitly set
+ if (hasCommand || hasLabelCommand) && !hasStatusComment && workflowData.StatusComment == nil {
+ trueVal := true
+ workflowData.StatusComment = &trueVal
+ }
+
// Store other events for merging in applyDefaults
if hasCommand && len(otherEvents) > 0 {
// We'll store this and handle it in applyDefaults
workflowData.On = "" // This will trigger command handling in applyDefaults
workflowData.CommandOtherEvents = otherEvents
+ } else if hasLabelCommand && len(otherEvents) > 0 {
+ // Store other events for label-command merging in applyDefaults
+ workflowData.On = "" // This will trigger label-command handling in applyDefaults
+ workflowData.LabelCommandOtherEvents = otherEvents
} else if (hasReaction || hasStopAfter || hasStatusComment) && len(otherEvents) > 0 {
// Only re-marshal the "on" if we have to
onEventsYAML, err := yaml.Marshal(map[string]any{"on": otherEvents})
diff --git a/pkg/workflow/compiler_safe_outputs_config.go b/pkg/workflow/compiler_safe_outputs_config.go
index d8e55fed4bc..259bcd039a8 100644
--- a/pkg/workflow/compiler_safe_outputs_config.go
+++ b/pkg/workflow/compiler_safe_outputs_config.go
@@ -586,6 +586,22 @@ var handlerRegistry = map[string]handlerBuilder{
builder.AddIfNotEmpty("github-token", c.GitHubToken)
return builder.Build()
},
+ "call_workflow": func(cfg *SafeOutputsConfig) map[string]any {
+ if cfg.CallWorkflow == nil {
+ return nil
+ }
+ c := cfg.CallWorkflow
+ builder := newHandlerConfigBuilder().
+ AddTemplatableInt("max", c.Max).
+ AddStringSlice("workflows", c.Workflows)
+
+ // Add workflow_files map if it has entries
+ if len(c.WorkflowFiles) > 0 {
+ builder.AddDefault("workflow_files", c.WorkflowFiles)
+ }
+
+ return builder.Build()
+ },
"missing_tool": func(cfg *SafeOutputsConfig) map[string]any {
if cfg.MissingTool == nil {
return nil
diff --git a/pkg/workflow/compiler_safe_outputs_config_test.go b/pkg/workflow/compiler_safe_outputs_config_test.go
index ee46f711a0c..5d91cf87e61 100644
--- a/pkg/workflow/compiler_safe_outputs_config_test.go
+++ b/pkg/workflow/compiler_safe_outputs_config_test.go
@@ -263,6 +263,23 @@ func TestAddHandlerManagerConfigEnvVar(t *testing.T) {
checkJSON: true,
expectedKeys: []string{"create_issue"},
},
+ {
+ name: "call_workflow config",
+ safeOutputs: &SafeOutputsConfig{
+ CallWorkflow: &CallWorkflowConfig{
+ BaseSafeOutputConfig: BaseSafeOutputConfig{
+ Max: strPtr("1"),
+ },
+ Workflows: []string{"worker-a", "worker-b"},
+ WorkflowFiles: map[string]string{"worker-a": "./.github/workflows/worker-a.lock.yml", "worker-b": "./.github/workflows/worker-b.lock.yml"},
+ },
+ },
+ checkContains: []string{
+ "GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG",
+ },
+ checkJSON: true,
+ expectedKeys: []string{"call_workflow"},
+ },
}
for _, tt := range tests {
@@ -1297,3 +1314,72 @@ func TestHandlerConfigStagedMode(t *testing.T) {
})
}
}
+
+// TestAddHandlerManagerConfigEnvVar_CallWorkflow asserts that GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG
+// contains call_workflow, workflows, and workflow_files when SafeOutputs.CallWorkflow is configured.
+func TestAddHandlerManagerConfigEnvVar_CallWorkflow(t *testing.T) {
+ compiler := NewCompiler()
+
+ workflowData := &WorkflowData{
+ Name: "Test Workflow",
+ SafeOutputs: &SafeOutputsConfig{
+ CallWorkflow: &CallWorkflowConfig{
+ BaseSafeOutputConfig: BaseSafeOutputConfig{
+ Max: strPtr("1"),
+ },
+ Workflows: []string{"worker-a", "worker-b"},
+ WorkflowFiles: map[string]string{"worker-a": "./.github/workflows/worker-a.lock.yml", "worker-b": "./.github/workflows/worker-b.lock.yml"},
+ },
+ },
+ }
+
+ var steps []string
+ compiler.addHandlerManagerConfigEnvVar(&steps, workflowData)
+
+ require.NotEmpty(t, steps, "Steps should not be empty")
+
+ var callWorkflowConfig map[string]any
+ for _, step := range steps {
+ if strings.Contains(step, "GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG") {
+ parts := strings.Split(step, "GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: ")
+ require.Len(t, parts, 2, "Should have two parts")
+
+ jsonStr := strings.TrimSpace(parts[1])
+ jsonStr = strings.Trim(jsonStr, "\"")
+ jsonStr = strings.ReplaceAll(jsonStr, "\\\"", "\"")
+
+ var config map[string]map[string]any
+ err := json.Unmarshal([]byte(jsonStr), &config)
+ require.NoError(t, err, "Handler config JSON should be valid")
+
+ cfg, ok := config["call_workflow"]
+ require.True(t, ok, "GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG should contain 'call_workflow' key")
+ callWorkflowConfig = cfg
+ break
+ }
+ }
+
+ require.NotNil(t, callWorkflowConfig, "call_workflow config should be present")
+
+ // Verify max
+ maxVal, ok := callWorkflowConfig["max"]
+ require.True(t, ok, "call_workflow config should have 'max' field")
+ assert.InDelta(t, float64(1), maxVal, 0.0001, "max should be 1")
+
+ // Verify workflows list
+ workflowsVal, ok := callWorkflowConfig["workflows"]
+ require.True(t, ok, "call_workflow config should have 'workflows' field")
+ workflowsSlice, ok := workflowsVal.([]any)
+ require.True(t, ok, "workflows should be an array")
+ assert.Len(t, workflowsSlice, 2, "Should have 2 workflows")
+ assert.Contains(t, workflowsSlice, "worker-a", "Should contain worker-a")
+ assert.Contains(t, workflowsSlice, "worker-b", "Should contain worker-b")
+
+ // Verify workflow_files map
+ filesVal, ok := callWorkflowConfig["workflow_files"]
+ require.True(t, ok, "call_workflow config should have 'workflow_files' field")
+ filesMap, ok := filesVal.(map[string]any)
+ require.True(t, ok, "workflow_files should be a map")
+ assert.Equal(t, "./.github/workflows/worker-a.lock.yml", filesMap["worker-a"], "worker-a path should match")
+ assert.Equal(t, "./.github/workflows/worker-b.lock.yml", filesMap["worker-b"], "worker-b path should match")
+}
diff --git a/pkg/workflow/compiler_safe_outputs_job.go b/pkg/workflow/compiler_safe_outputs_job.go
index f376b452eb8..71b9ac09574 100644
--- a/pkg/workflow/compiler_safe_outputs_job.go
+++ b/pkg/workflow/compiler_safe_outputs_job.go
@@ -134,6 +134,7 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa
data.SafeOutputs.HideComment != nil ||
data.SafeOutputs.SetIssueType != nil ||
data.SafeOutputs.DispatchWorkflow != nil ||
+ data.SafeOutputs.CallWorkflow != nil ||
data.SafeOutputs.CreateCodeScanningAlerts != nil ||
data.SafeOutputs.AutofixCodeScanningAlert != nil ||
data.SafeOutputs.MissingTool != nil ||
@@ -265,10 +266,8 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa
// Count setup action steps (checkout + setup if in dev mode without action-tag, or just setup)
setupActionRef := c.resolveActionReference("./actions/setup", data)
if setupActionRef != "" {
- if len(c.generateCheckoutActionsFolder(data)) > 0 {
- insertIndex += 6 // Checkout step (6 lines: name, uses, with, sparse-checkout header, actions, persist-credentials)
- }
- insertIndex += 4 // Setup step (4 lines: name, uses, with, destination)
+ insertIndex += len(c.generateCheckoutActionsFolder(data))
+ insertIndex += len(c.generateSetupStep(setupActionRef, SetupActionDestination, c.hasCustomTokenSafeOutputs(data.SafeOutputs)))
}
// Add artifact download steps count
@@ -385,6 +384,10 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa
func (c *Compiler) buildJobLevelSafeOutputEnvVars(data *WorkflowData, workflowID string) map[string]string {
envVars := make(map[string]string)
+ // Set GH_AW_HOME so steps can use $GH_AW_HOME without the :-fallback syntax.
+ // Use GhAwHomeExprDefault so callers can override via workflow/repo env.
+ envVars["GH_AW_HOME"] = GhAwHomeExprDefault
+
// Set GH_AW_WORKFLOW_ID to the workflow ID (filename without extension)
// This is used for branch naming in create_pull_request and other operations
envVars["GH_AW_WORKFLOW_ID"] = fmt.Sprintf("%q", workflowID)
diff --git a/pkg/workflow/compiler_safe_outputs_job_test.go b/pkg/workflow/compiler_safe_outputs_job_test.go
index 4e7739003de..024209119fd 100644
--- a/pkg/workflow/compiler_safe_outputs_job_test.go
+++ b/pkg/workflow/compiler_safe_outputs_job_test.go
@@ -737,3 +737,32 @@ func TestConclusionJobWithGitHubAppWorkflowCallUsesTargetRepoNameFallback(t *tes
assert.NotContains(t, stepsContent, "repositories: ${{ needs.activation.outputs.target_repo }}",
"Conclusion job GitHub App token step must not use target_repo (full slug) for workflow_call workflows")
}
+
+// TestCallWorkflowOnly_UsesHandlerManagerStep asserts that a workflow configured with only
+// call-workflow (no other handler-manager types) still compiles a "Process Safe Outputs" step.
+func TestCallWorkflowOnly_UsesHandlerManagerStep(t *testing.T) {
+ compiler := NewCompiler()
+ compiler.jobManager = NewJobManager()
+
+ workflowData := &WorkflowData{
+ Name: "Test Workflow",
+ SafeOutputs: &SafeOutputsConfig{
+ CallWorkflow: &CallWorkflowConfig{
+ BaseSafeOutputConfig: BaseSafeOutputConfig{
+ Max: strPtr("1"),
+ },
+ Workflows: []string{"worker-a"},
+ },
+ },
+ }
+
+ job, stepNames, err := compiler.buildConsolidatedSafeOutputsJob(workflowData, string(constants.AgentJobName), "test-workflow.md")
+ require.NoError(t, err, "Should compile without error")
+ require.NotNil(t, job, "safe_outputs job should be generated when only call-workflow is configured")
+ require.NotNil(t, stepNames, "Step names should not be nil")
+
+ stepsContent := strings.Join(job.Steps, "")
+ assert.Contains(t, stepsContent, "Process Safe Outputs", "Compiled job should include 'Process Safe Outputs' step")
+ assert.Contains(t, stepsContent, "GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG", "Compiled job should include handler config env var")
+ assert.Contains(t, stepsContent, "call_workflow", "Handler config should reference call_workflow")
+}
diff --git a/pkg/workflow/compiler_safe_outputs_specialized.go b/pkg/workflow/compiler_safe_outputs_specialized.go
index 9217585f8d5..4e304574359 100644
--- a/pkg/workflow/compiler_safe_outputs_specialized.go
+++ b/pkg/workflow/compiler_safe_outputs_specialized.go
@@ -127,7 +127,7 @@ func (c *Compiler) buildCreateAgentSessionStepConfig(data *WorkflowData, mainJob
return SafeOutputStepConfig{
StepName: "Create Agent Session",
StepID: "create_agent_session",
- Script: "const { main } = require('/opt/gh-aw/actions/create_agent_session.cjs'); await main();",
+ Script: "const { main } = require(" + JsRequireGhAw("actions/create_agent_session.cjs") + "); await main();",
CustomEnvVars: customEnvVars,
Condition: condition,
Token: cfg.GitHubToken,
diff --git a/pkg/workflow/compiler_safe_outputs_steps.go b/pkg/workflow/compiler_safe_outputs_steps.go
index d67426dbd7a..383dd82cc05 100644
--- a/pkg/workflow/compiler_safe_outputs_steps.go
+++ b/pkg/workflow/compiler_safe_outputs_steps.go
@@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"sort"
- "strings"
"github.com/github/gh-aw/pkg/logger"
"github.com/github/gh-aw/pkg/stringutil"
@@ -70,11 +69,11 @@ func computeEffectivePRCheckoutToken(safeOutputs *SafeOutputsConfig) (token stri
// 2. Safe-outputs level token
// 3. Magic secret fallback via getEffectiveProjectGitHubToken()
func computeEffectiveProjectToken(perConfigToken string, safeOutputsToken string) string {
- configToken := perConfigToken
- if configToken == "" && safeOutputsToken != "" {
- configToken = safeOutputsToken
+ token := perConfigToken
+ if token == "" {
+ token = safeOutputsToken
}
- return getEffectiveProjectGitHubToken(configToken)
+ return getEffectiveProjectGitHubToken(token)
}
// computeProjectURLAndToken computes the project URL and token from the various project-related
@@ -159,13 +158,13 @@ func (c *Compiler) buildConsolidatedSafeOutputStep(data *WorkflowData, config Sa
// Use require mode if ScriptName is set, otherwise inline the bundled script
if config.ScriptName != "" {
// Require mode: Use setup_globals helper
- steps = append(steps, " const { setupGlobals } = require('"+SetupActionDestination+"/setup_globals.cjs');\n")
+ steps = append(steps, " const { setupGlobals } = require("+JsRequireGhAw("actions/setup_globals.cjs")+");\n")
steps = append(steps, " setupGlobals(core, github, context, exec, io);\n")
- steps = append(steps, fmt.Sprintf(" const { main } = require('"+SetupActionDestination+"/%s.cjs');\n", config.ScriptName))
+ steps = append(steps, " const { main } = require("+JsRequireGhAw("actions/"+config.ScriptName+".cjs")+");\n")
steps = append(steps, " await main();\n")
} else {
// Inline JavaScript: Use setup_globals helper
- steps = append(steps, " const { setupGlobals } = require('"+SetupActionDestination+"/setup_globals.cjs');\n")
+ steps = append(steps, " const { setupGlobals } = require("+JsRequireGhAw("actions/setup_globals.cjs")+");\n")
steps = append(steps, " setupGlobals(core, github, context, exec, io);\n")
// Inline mode: embed the bundled script directly
formattedScript := FormatJavaScriptForYAML(config.Script)
@@ -320,7 +319,8 @@ func (c *Compiler) buildHandlerManagerStep(data *WorkflowData) []string {
// default GitHub domains, causing user-configured allowed domains to be redacted.
var domainsStr string
if data.SafeOutputs != nil && len(data.SafeOutputs.AllowedDomains) > 0 {
- domainsStr = strings.Join(data.SafeOutputs.AllowedDomains, ",")
+ // allowed-domains: additional domains unioned with engine/network base set; supports ecosystem identifiers
+ domainsStr = c.computeExpandedAllowedDomainsForSanitization(data)
} else {
domainsStr = c.computeAllowedDomainsForSanitization(data)
}
@@ -437,9 +437,9 @@ func (c *Compiler) buildHandlerManagerStep(data *WorkflowData) []string {
c.addSafeOutputGitHubTokenForConfig(&steps, data, configToken)
steps = append(steps, " script: |\n")
- steps = append(steps, " const { setupGlobals } = require('"+SetupActionDestination+"/setup_globals.cjs');\n")
+ steps = append(steps, " const { setupGlobals } = require("+JsRequireGhAw("actions/setup_globals.cjs")+");\n")
steps = append(steps, " setupGlobals(core, github, context, exec, io);\n")
- steps = append(steps, " const { main } = require('"+SetupActionDestination+"/safe_output_handler_manager.cjs');\n")
+ steps = append(steps, " const { main } = require("+JsRequireGhAw("actions/safe_output_handler_manager.cjs")+");\n")
steps = append(steps, " await main();\n")
return steps
diff --git a/pkg/workflow/compiler_safe_outputs_steps_test.go b/pkg/workflow/compiler_safe_outputs_steps_test.go
index 3e70e9d29db..93ee53b7b4f 100644
--- a/pkg/workflow/compiler_safe_outputs_steps_test.go
+++ b/pkg/workflow/compiler_safe_outputs_steps_test.go
@@ -47,7 +47,7 @@ func TestBuildConsolidatedSafeOutputStep(t *testing.T) {
"name: Create Issue",
"id: create_issue",
"setupGlobals",
- "require('/opt/gh-aw/actions/create_issue_handler.cjs')",
+ "require(" + JsRequireGhAw("actions/create_issue_handler.cjs") + ")",
"await main();",
},
checkNotContains: []string{
@@ -508,7 +508,9 @@ func TestBuildHandlerManagerStep(t *testing.T) {
AddComments: &AddCommentsConfig{},
},
checkContains: []string{
- "GH_AW_ALLOWED_DOMAINS: \"docs.example.com,api.example.com\"",
+ "GH_AW_ALLOWED_DOMAINS:",
+ "docs.example.com",
+ "api.example.com",
"GITHUB_SERVER_URL: ${{ github.server_url }}",
"GITHUB_API_URL: ${{ github.api_url }}",
},
@@ -749,7 +751,7 @@ func TestScriptNameVsInlineScript(t *testing.T) {
stepsContent := strings.Join(steps, "")
assert.Contains(t, stepsContent, "setupGlobals")
- assert.Contains(t, stepsContent, "require('/opt/gh-aw/actions/test_handler.cjs')")
+ assert.Contains(t, stepsContent, "require("+JsRequireGhAw("actions/test_handler.cjs")+")")
assert.Contains(t, stepsContent, "await main()")
assert.NotContains(t, stepsContent, "console.log")
})
diff --git a/pkg/workflow/compiler_types.go b/pkg/workflow/compiler_types.go
index 0812bd4a6be..c1e81d5e6a7 100644
--- a/pkg/workflow/compiler_types.go
+++ b/pkg/workflow/compiler_types.go
@@ -381,10 +381,15 @@ type WorkflowData struct {
SkipIfNoMatch *SkipIfNoMatchConfig // skip-if-no-match configuration with query and min threshold
SkipRoles []string // roles to skip workflow for (e.g., [admin, maintainer, write])
SkipBots []string // users to skip workflow for (e.g., [user1, user2])
+ OnSteps []map[string]any // steps to inject into the pre-activation job from on.steps
+ OnPermissions *Permissions // additional permissions for the pre-activation job from on.permissions
ManualApproval string // environment name for manual approval from on: section
Command []string // for /command trigger support - multiple command names
CommandEvents []string // events where command should be active (nil = all events)
CommandOtherEvents map[string]any // for merging command with other events
+ LabelCommand []string // for label-command trigger support - label names that act as commands
+ LabelCommandEvents []string // events where label-command should be active (nil = all: issues, pull_request, discussion)
+ LabelCommandOtherEvents map[string]any // for merging label-command with other events
AIReaction string // AI reaction type like "eyes", "heart", etc.
StatusComment *bool // whether to post status comments (default: true when ai-reaction is set, false otherwise)
ActivationGitHubToken string // custom github token from on.github-token for reactions/comments
@@ -477,25 +482,25 @@ type SafeOutputsConfig struct {
ThreatDetection *ThreatDetectionConfig `yaml:"threat-detection,omitempty"` // Threat detection configuration
Jobs map[string]*SafeJobConfig `yaml:"jobs,omitempty"` // Safe-jobs configuration (moved from top-level)
GitHubApp *GitHubAppConfig `yaml:"github-app,omitempty"` // GitHub App credentials for token minting
- AllowedDomains []string `yaml:"allowed-domains,omitempty"`
- AllowGitHubReferences []string `yaml:"allowed-github-references,omitempty"` // Allowed repositories for GitHub references (e.g., ["repo", "org/repo2"])
- Staged bool `yaml:"staged,omitempty"` // If true, emit step summary messages instead of making GitHub API calls
- Env map[string]string `yaml:"env,omitempty"` // Environment variables to pass to safe output jobs
- GitHubToken string `yaml:"github-token,omitempty"` // GitHub token for safe output jobs
- MaximumPatchSize int `yaml:"max-patch-size,omitempty"` // Maximum allowed patch size in KB (defaults to 1024)
- RunsOn string `yaml:"runs-on,omitempty"` // Runner configuration for safe-outputs jobs
- Messages *SafeOutputMessagesConfig `yaml:"messages,omitempty"` // Custom message templates for footer and notifications
- Mentions *MentionsConfig `yaml:"mentions,omitempty"` // Configuration for @mention filtering in safe outputs
- Footer *bool `yaml:"footer,omitempty"` // Global footer control - when false, omits visible footer from all safe outputs (XML markers still included)
- GroupReports bool `yaml:"group-reports,omitempty"` // If true, create parent "Failed runs" issue for agent failures (default: false)
- ReportFailureAsIssue *bool `yaml:"report-failure-as-issue,omitempty"` // If false, disables creating failure tracking issues when workflows fail (default: true)
- FailureIssueRepo string `yaml:"failure-issue-repo,omitempty"` // Repository to create failure issues in (format: "owner/repo"), defaults to current repo
- MaxBotMentions *string `yaml:"max-bot-mentions,omitempty"` // Maximum bot trigger references (e.g. 'fixes #123') allowed before filtering. Default: 10. Supports integer or GitHub Actions expression.
- Steps []any `yaml:"steps,omitempty"` // User-provided steps injected after setup/checkout and before safe-output code
- IDToken *string `yaml:"id-token,omitempty"` // Override id-token permission: "write" to force-add, "none" to disable auto-detection
- ConcurrencyGroup string `yaml:"concurrency-group,omitempty"` // Concurrency group for the safe-outputs job (cancel-in-progress is always false)
- Environment string `yaml:"environment,omitempty"` // Override the GitHub deployment environment for the safe-outputs job (defaults to the top-level environment: field)
- AutoInjectedCreateIssue bool `yaml:"-"` // Internal: true when create-issues was automatically injected by the compiler (not user-configured)
+ AllowedDomains []string `yaml:"allowed-domains,omitempty"` // Allowed domains for URL redaction, unioned with network.allowed; supports ecosystem identifiers
+ AllowGitHubReferences []string `yaml:"allowed-github-references,omitempty"` // Allowed repositories for GitHub references (e.g., ["repo", "org/repo2"])
+ Staged bool `yaml:"staged,omitempty"` // If true, emit step summary messages instead of making GitHub API calls
+ Env map[string]string `yaml:"env,omitempty"` // Environment variables to pass to safe output jobs
+ GitHubToken string `yaml:"github-token,omitempty"` // GitHub token for safe output jobs
+ MaximumPatchSize int `yaml:"max-patch-size,omitempty"` // Maximum allowed patch size in KB (defaults to 1024)
+ RunsOn string `yaml:"runs-on,omitempty"` // Runner configuration for safe-outputs jobs
+ Messages *SafeOutputMessagesConfig `yaml:"messages,omitempty"` // Custom message templates for footer and notifications
+ Mentions *MentionsConfig `yaml:"mentions,omitempty"` // Configuration for @mention filtering in safe outputs
+ Footer *bool `yaml:"footer,omitempty"` // Global footer control - when false, omits visible footer from all safe outputs (XML markers still included)
+ GroupReports bool `yaml:"group-reports,omitempty"` // If true, create parent "Failed runs" issue for agent failures (default: false)
+ ReportFailureAsIssue *bool `yaml:"report-failure-as-issue,omitempty"` // If false, disables creating failure tracking issues when workflows fail (default: true)
+ FailureIssueRepo string `yaml:"failure-issue-repo,omitempty"` // Repository to create failure issues in (format: "owner/repo"), defaults to current repo
+ MaxBotMentions *string `yaml:"max-bot-mentions,omitempty"` // Maximum bot trigger references (e.g. 'fixes #123') allowed before filtering. Default: 10. Supports integer or GitHub Actions expression.
+ Steps []any `yaml:"steps,omitempty"` // User-provided steps injected after setup/checkout and before safe-output code
+ IDToken *string `yaml:"id-token,omitempty"` // Override id-token permission: "write" to force-add, "none" to disable auto-detection
+ ConcurrencyGroup string `yaml:"concurrency-group,omitempty"` // Concurrency group for the safe-outputs job (cancel-in-progress is always false)
+ Environment string `yaml:"environment,omitempty"` // Override the GitHub deployment environment for the safe-outputs job (defaults to the top-level environment: field)
+ AutoInjectedCreateIssue bool `yaml:"-"` // Internal: true when create-issues was automatically injected by the compiler (not user-configured)
}
// SafeOutputMessagesConfig holds custom message templates for safe-output footer and notification messages
diff --git a/pkg/workflow/compiler_workflow_call.go b/pkg/workflow/compiler_workflow_call.go
index 8cb8fe0db67..a37136cb356 100644
--- a/pkg/workflow/compiler_workflow_call.go
+++ b/pkg/workflow/compiler_workflow_call.go
@@ -47,7 +47,7 @@ func generateArtifactPrefixStep() []string {
" id: artifact-prefix\n",
" env:\n",
" INPUTS_JSON: ${{ toJSON(inputs) }}\n",
- " run: bash /opt/gh-aw/actions/compute_artifact_prefix.sh\n",
+ " run: bash " + GhAwHome + "/actions/compute_artifact_prefix.sh\n",
}
}
diff --git a/pkg/workflow/compiler_yaml.go b/pkg/workflow/compiler_yaml.go
index 918de3a9495..c5f9775d7f5 100644
--- a/pkg/workflow/compiler_yaml.go
+++ b/pkg/workflow/compiler_yaml.go
@@ -508,13 +508,13 @@ func (c *Compiler) generatePrompt(yaml *strings.Builder, data *WorkflowData, pre
yaml.WriteString(" - name: Validate prompt placeholders\n")
yaml.WriteString(" env:\n")
yaml.WriteString(" GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt\n")
- yaml.WriteString(" run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh\n")
+ yaml.WriteString(" run: bash " + GhAwHome + "/actions/validate_prompt_placeholders.sh\n")
// Print prompt (merged into prompt generation)
yaml.WriteString(" - name: Print prompt\n")
yaml.WriteString(" env:\n")
yaml.WriteString(" GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt\n")
- yaml.WriteString(" run: bash /opt/gh-aw/actions/print_prompt_summary.sh\n")
+ yaml.WriteString(" run: bash " + GhAwHome + "/actions/print_prompt_summary.sh\n")
}
func (c *Compiler) generatePostSteps(yaml *strings.Builder, data *WorkflowData) {
if data.PostSteps != "" {
@@ -611,6 +611,15 @@ func (c *Compiler) generateCreateAwInfo(yaml *strings.Builder, data *WorkflowDat
mcpGatewayVersion = data.SandboxConfig.MCP.Version
}
+ // APM version
+ apmVersion := ""
+ if data.APMDependencies != nil && len(data.APMDependencies.Packages) > 0 {
+ apmVersion = data.APMDependencies.Version
+ if apmVersion == "" {
+ apmVersion = string(constants.DefaultAPMVersion)
+ }
+ }
+
// Firewall type
firewallType := ""
if isFirewallEnabled(data) {
@@ -641,6 +650,9 @@ func (c *Compiler) generateCreateAwInfo(yaml *strings.Builder, data *WorkflowDat
fmt.Fprintf(yaml, " GH_AW_INFO_FIREWALL_ENABLED: \"%t\"\n", firewallEnabled)
fmt.Fprintf(yaml, " GH_AW_INFO_AWF_VERSION: \"%s\"\n", firewallVersion)
fmt.Fprintf(yaml, " GH_AW_INFO_AWMG_VERSION: \"%s\"\n", mcpGatewayVersion)
+ if apmVersion != "" {
+ fmt.Fprintf(yaml, " GH_AW_INFO_APM_VERSION: \"%s\"\n", apmVersion)
+ }
fmt.Fprintf(yaml, " GH_AW_INFO_FIREWALL_TYPE: \"%s\"\n", firewallType)
// Always include strict mode flag for lockdown validation.
// validateLockdownRequirements uses this to enforce strict: true for public repositories.
@@ -665,7 +677,7 @@ func (c *Compiler) generateCreateAwInfo(yaml *strings.Builder, data *WorkflowDat
fmt.Fprintf(yaml, " uses: %s\n", GetActionPin("actions/github-script"))
yaml.WriteString(" with:\n")
yaml.WriteString(" script: |\n")
- yaml.WriteString(" const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/generate_aw_info.cjs") + ");\n")
yaml.WriteString(" await main(core, context);\n")
}
@@ -693,8 +705,8 @@ func (c *Compiler) generateOutputCollectionStep(yaml *strings.Builder, data *Wor
// Use manually configured domains if available, otherwise compute from network configuration
var domainsStr string
if data.SafeOutputs != nil && len(data.SafeOutputs.AllowedDomains) > 0 {
- // Use manually configured allowed domains
- domainsStr = strings.Join(data.SafeOutputs.AllowedDomains, ",")
+ // allowed-domains: additional domains unioned with engine/network base set; supports ecosystem identifiers
+ domainsStr = c.computeExpandedAllowedDomainsForSanitization(data)
} else {
// Fall back to computing from network configuration (same as firewall)
domainsStr = c.computeAllowedDomainsForSanitization(data)
@@ -724,9 +736,9 @@ func (c *Compiler) generateOutputCollectionStep(yaml *strings.Builder, data *Wor
yaml.WriteString(" script: |\n")
// Load script from external file using require()
- yaml.WriteString(" const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');\n")
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
- yaml.WriteString(" const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/collect_ndjson_output.cjs") + ");\n")
yaml.WriteString(" await main();\n")
}
diff --git a/pkg/workflow/compiler_yaml_ai_execution.go b/pkg/workflow/compiler_yaml_ai_execution.go
index c0ac972ff18..07c8aaa504b 100644
--- a/pkg/workflow/compiler_yaml_ai_execution.go
+++ b/pkg/workflow/compiler_yaml_ai_execution.go
@@ -47,10 +47,10 @@ func (c *Compiler) generateLogParsing(yaml *strings.Builder, engine CodingAgentE
yaml.WriteString(" script: |\n")
// Use the setup_globals helper to store GitHub Actions objects in global scope
- yaml.WriteString(" const { setupGlobals } = require('" + SetupActionDestination + "/setup_globals.cjs');\n")
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
// Load log parser script from external file using require()
- yaml.WriteString(" const { main } = require('/opt/gh-aw/actions/" + parserScriptName + ".cjs');\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/"+parserScriptName+".cjs") + ");\n")
yaml.WriteString(" await main();\n")
}
@@ -65,10 +65,10 @@ func (c *Compiler) generateMCPScriptsLogParsing(yaml *strings.Builder) {
yaml.WriteString(" script: |\n")
// Use the setup_globals helper to store GitHub Actions objects in global scope
- yaml.WriteString(" const { setupGlobals } = require('" + SetupActionDestination + "/setup_globals.cjs');\n")
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
// Load mcp-scripts log parser script from external file using require()
- yaml.WriteString(" const { main } = require('/opt/gh-aw/actions/parse_mcp_scripts_logs.cjs');\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/parse_mcp_scripts_logs.cjs") + ");\n")
yaml.WriteString(" await main();\n")
}
@@ -83,10 +83,10 @@ func (c *Compiler) generateMCPGatewayLogParsing(yaml *strings.Builder) {
yaml.WriteString(" script: |\n")
// Use the setup_globals helper to store GitHub Actions objects in global scope
- yaml.WriteString(" const { setupGlobals } = require('" + SetupActionDestination + "/setup_globals.cjs');\n")
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
// Load MCP gateway log parser script from external file using require()
- yaml.WriteString(" const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/parse_mcp_gateway_log.cjs") + ");\n")
yaml.WriteString(" await main();\n")
}
@@ -108,7 +108,7 @@ func (c *Compiler) generateStopMCPGateway(yaml *strings.Builder, data *WorkflowD
yaml.WriteString(" GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}\n")
yaml.WriteString(" run: |\n")
- yaml.WriteString(" bash /opt/gh-aw/actions/stop_mcp_gateway.sh \"$GATEWAY_PID\"\n")
+ yaml.WriteString(" bash " + GhAwHome + "/actions/stop_mcp_gateway.sh \"$GATEWAY_PID\"\n")
}
// generateAgentStepSummaryAppend generates a step that appends the agent's GITHUB_STEP_SUMMARY
@@ -120,5 +120,5 @@ func (c *Compiler) generateAgentStepSummaryAppend(yaml *strings.Builder) {
yaml.WriteString(" - name: Append agent step summary\n")
yaml.WriteString(" if: always()\n")
- yaml.WriteString(" run: bash /opt/gh-aw/actions/append_agent_step_summary.sh\n")
+ yaml.WriteString(" run: bash " + GhAwHome + "/actions/append_agent_step_summary.sh\n")
}
diff --git a/pkg/workflow/compiler_yaml_helpers.go b/pkg/workflow/compiler_yaml_helpers.go
index 9b65312428a..1d97f6d04c4 100644
--- a/pkg/workflow/compiler_yaml_helpers.go
+++ b/pkg/workflow/compiler_yaml_helpers.go
@@ -218,11 +218,11 @@ func generatePlaceholderSubstitutionStep(yaml *strings.Builder, expressionMappin
yaml.WriteString(indent + " script: |\n")
// Use setup_globals helper to make GitHub Actions objects available globally
- yaml.WriteString(indent + " const { setupGlobals } = require('" + SetupActionDestination + "/setup_globals.cjs');\n")
+ yaml.WriteString(indent + " const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
yaml.WriteString(indent + " setupGlobals(core, github, context, exec, io);\n")
yaml.WriteString(indent + " \n")
// Use require() to load script from copied files
- yaml.WriteString(indent + " const substitutePlaceholders = require('" + SetupActionDestination + "/substitute_placeholders.cjs');\n")
+ yaml.WriteString(indent + " const substitutePlaceholders = require(" + JsRequireGhAw("actions/substitute_placeholders.cjs") + ");\n")
yaml.WriteString(indent + " \n")
yaml.WriteString(indent + " // Call the substitution function\n")
yaml.WriteString(indent + " return await substitutePlaceholders({\n")
@@ -354,9 +354,9 @@ func generateGitHubScriptWithRequire(scriptPath string) string {
var script strings.Builder
// Use the setup_globals helper to store GitHub Actions objects in global scope
- script.WriteString(" const { setupGlobals } = require('" + SetupActionDestination + "/setup_globals.cjs');\n")
+ script.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
script.WriteString(" setupGlobals(core, github, context, exec, io);\n")
- script.WriteString(" const { main } = require('" + SetupActionDestination + "/" + scriptPath + "');\n")
+ script.WriteString(" const { main } = require(" + JsRequireGhAw("actions/"+scriptPath) + ");\n")
script.WriteString(" await main();\n")
return script.String()
@@ -392,7 +392,8 @@ func generateInlineGitHubScriptStep(stepName, script, condition string) string {
//
// Parameters:
// - setupActionRef: The action reference for setup action (e.g., "./actions/setup" or "github/gh-aw/actions/setup@sha")
-// - destination: The destination path where files should be copied (e.g., SetupActionDestination)
+// - destination: The destination path where files should be copied (e.g., SetupActionDestination).
+// This is passed as INPUT_DESTINATION in script mode or as the destination: input in dev/release mode.
// - enableCustomTokens: Whether to enable custom-token support (installs @actions/github so handler_auth.cjs can create per-handler Octokit clients)
//
// Returns a slice of strings representing the YAML lines for the setup step.
@@ -412,7 +413,8 @@ func (c *Compiler) generateSetupStep(setupActionRef string, destination string,
return lines
}
- // Dev/Release mode: use the setup action
+ // Dev/Release mode: use the setup action.
+ // Pass destination so callers can relocate GH_AW_HOME (e.g. on self-hosted runners).
lines := []string{
" - name: Setup Scripts\n",
fmt.Sprintf(" uses: %s\n", setupActionRef),
diff --git a/pkg/workflow/compiler_yaml_main_job.go b/pkg/workflow/compiler_yaml_main_job.go
index 48fdbe037aa..14f10a2ab84 100644
--- a/pkg/workflow/compiler_yaml_main_job.go
+++ b/pkg/workflow/compiler_yaml_main_job.go
@@ -117,9 +117,9 @@ func (c *Compiler) generateMainJobSteps(yaml *strings.Builder, data *WorkflowDat
yaml.WriteString(" with:\n")
yaml.WriteString(" script: |\n")
- yaml.WriteString(" const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');\n")
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
- yaml.WriteString(" const { main } = require('/opt/gh-aw/actions/merge_remote_agent_github_folder.cjs');\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/merge_remote_agent_github_folder.cjs") + ");\n")
yaml.WriteString(" await main();\n")
}
@@ -178,7 +178,11 @@ func (c *Compiler) generateMainJobSteps(yaml *strings.Builder, data *WorkflowDat
// Create /tmp/gh-aw/ base directory for all temporary files
// This must be created before custom steps so they can use the temp directory
yaml.WriteString(" - name: Create gh-aw temp directory\n")
- yaml.WriteString(" run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh\n")
+ yaml.WriteString(" run: |\n")
+ yaml.WriteString(" bash " + GhAwHome + "/actions/create_gh_aw_tmp_dir.sh\n")
+ yaml.WriteString(" echo \"GH_AW_SAFE_OUTPUTS=" + GhAwHome + "/safeoutputs/outputs.jsonl\" >> \"$GITHUB_ENV\"\n")
+ yaml.WriteString(" echo \"GH_AW_SAFE_OUTPUTS_CONFIG_PATH=" + GhAwHome + "/safeoutputs/config.json\" >> \"$GITHUB_ENV\"\n")
+ yaml.WriteString(" echo \"GH_AW_SAFE_OUTPUTS_TOOLS_PATH=" + GhAwHome + "/safeoutputs/tools.json\" >> \"$GITHUB_ENV\"\n")
// Add custom steps if present
if data.CustomSteps != "" {
@@ -206,10 +210,18 @@ func (c *Compiler) generateMainJobSteps(yaml *strings.Builder, data *WorkflowDat
compilerYamlLog.Printf("Generating repo-memory steps for workflow")
generateRepoMemorySteps(yaml, data)
- // Configure git credentials for agentic workflows
- gitConfigSteps := c.generateGitConfigurationSteps()
- for _, line := range gitConfigSteps {
- yaml.WriteString(line)
+ // Configure git credentials for agentic workflows.
+ // Git credential configuration requires a .git directory in the workspace, which is only
+ // present when the repository was checked out. Skip these steps when checkout is disabled
+ // and no custom steps perform a checkout, since git remote set-url origin would fail
+ // with "fatal: not a git repository" otherwise.
+ needsGitConfig := needsCheckout || customStepsContainCheckout
+ compilerYamlLog.Printf("Git credential configuration needed: %t (needsCheckout=%t, customStepsContainCheckout=%t)", needsGitConfig, needsCheckout, customStepsContainCheckout)
+ if needsGitConfig {
+ gitConfigSteps := c.generateGitConfigurationSteps()
+ for _, line := range gitConfigSteps {
+ yaml.WriteString(line)
+ }
}
// Add step to checkout PR branch if the event is pull_request
@@ -331,10 +343,13 @@ func (c *Compiler) generateMainJobSteps(yaml *strings.Builder, data *WorkflowDat
// Regenerate git credentials after agent execution
// This allows safe-outputs operations (like create_pull_request) to work properly
- // We regenerate the credentials rather than restoring from backup
- gitConfigStepsAfterAgent := c.generateGitConfigurationSteps()
- for _, line := range gitConfigStepsAfterAgent {
- yaml.WriteString(line)
+ // We regenerate the credentials rather than restoring from backup.
+ // Only emit these steps when a checkout was performed (requires a .git directory).
+ if needsGitConfig {
+ gitConfigStepsAfterAgent := c.generateGitConfigurationSteps()
+ for _, line := range gitConfigStepsAfterAgent {
+ yaml.WriteString(line)
+ }
}
// Collect firewall logs BEFORE secret redaction so secrets in logs can be redacted
diff --git a/pkg/workflow/compiler_yaml_main_job_test.go b/pkg/workflow/compiler_yaml_main_job_test.go
index 62fcc97b3fd..f4c9b82a1ba 100644
--- a/pkg/workflow/compiler_yaml_main_job_test.go
+++ b/pkg/workflow/compiler_yaml_main_job_test.go
@@ -558,7 +558,8 @@ func TestGenerateMainJobSteps(t *testing.T) {
"- name: Checkout repository",
"persist-credentials: false",
"- name: Create gh-aw temp directory",
- "run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh",
+ "run: |",
+ "bash " + GhAwHome + "/actions/create_gh_aw_tmp_dir.sh",
},
shouldError: false,
},
diff --git a/pkg/workflow/concurrency_validation.go b/pkg/workflow/concurrency_validation.go
index f8cfe0b1440..528f8ee048a 100644
--- a/pkg/workflow/concurrency_validation.go
+++ b/pkg/workflow/concurrency_validation.go
@@ -90,10 +90,12 @@ func validateConcurrencyGroupExpression(group string) error {
// such values are rare in concurrency group expressions, and any resulting syntax errors will be
// caught by the subsequent expression validation.
func extractConcurrencyGroupFromYAML(concurrencyYAML string) string {
+ concurrencyValidationLog.Printf("Extracting concurrency group from YAML (%d bytes)", len(concurrencyYAML))
// First, check if it's object format with explicit "group:" field
// Pattern: group: "value" or group: 'value' or group: value (at start of line or after spaces)
matches := concurrencyGroupPattern.FindStringSubmatch(concurrencyYAML)
if len(matches) > 1 {
+ concurrencyValidationLog.Printf("Extracted concurrency group from object format: %q", strings.TrimSpace(matches[1]))
return strings.TrimSpace(matches[1])
}
@@ -108,9 +110,11 @@ func extractConcurrencyGroupFromYAML(concurrencyYAML string) string {
value := strings.TrimSpace(after)
// Remove quotes if present
value = strings.Trim(value, `"'`)
+ concurrencyValidationLog.Printf("Extracted concurrency group from string format: %q", value)
return value
}
}
+ concurrencyValidationLog.Print("No concurrency group found in YAML")
return ""
}
diff --git a/pkg/workflow/copilot_engine_execution.go b/pkg/workflow/copilot_engine_execution.go
index ceeeb0d406e..533f10c622b 100644
--- a/pkg/workflow/copilot_engine_execution.go
+++ b/pkg/workflow/copilot_engine_execution.go
@@ -179,6 +179,10 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st
// Build AWF-wrapped command using helper function - no mkdir needed, AWF handles it
// Get allowed domains (copilot defaults + network permissions + HTTP MCP server URLs + runtime ecosystem domains)
allowedDomains := GetCopilotAllowedDomainsWithToolsAndRuntimes(workflowData.NetworkPermissions, workflowData.Tools, workflowData.Runtimes)
+ // Add GHES/custom API target domains to the firewall allow-list when engine.api-target is set
+ if workflowData.EngineConfig != nil && workflowData.EngineConfig.APITarget != "" {
+ allowedDomains = mergeAPITargetDomains(allowedDomains, workflowData.EngineConfig.APITarget)
+ }
// AWF v0.15.0+ uses chroot mode by default, providing transparent access to host binaries
// AWF v0.15.0+ with --env-all handles PATH natively (chroot mode is default):
@@ -404,7 +408,7 @@ func generateInferenceAccessErrorDetectionStep() GitHubActionStep {
step = append(step, " id: detect-inference-error")
step = append(step, " if: always()")
step = append(step, " continue-on-error: true")
- step = append(step, " run: bash /opt/gh-aw/actions/detect_inference_access_error.sh")
+ step = append(step, " run: bash "+GhAwHome+"/actions/detect_inference_access_error.sh")
return GitHubActionStep(step)
}
diff --git a/pkg/workflow/copilot_engine_installation.go b/pkg/workflow/copilot_engine_installation.go
index 06062185fc0..fd404608d91 100644
--- a/pkg/workflow/copilot_engine_installation.go
+++ b/pkg/workflow/copilot_engine_installation.go
@@ -161,7 +161,7 @@ func generateAWFInstallationStep(version string, agentConfig *AgentSandboxConfig
stepLines := []string{
" - name: Install AWF binary",
- " run: bash /opt/gh-aw/actions/install_awf_binary.sh " + version,
+ " run: bash " + GhAwHome + "/actions/install_awf_binary.sh " + version,
}
return GitHubActionStep(stepLines)
diff --git a/pkg/workflow/copilot_installer.go b/pkg/workflow/copilot_installer.go
index a5575274250..fa349ce3c92 100644
--- a/pkg/workflow/copilot_installer.go
+++ b/pkg/workflow/copilot_installer.go
@@ -27,7 +27,7 @@ func GenerateCopilotInstallerSteps(version, stepName string) []GitHubActionStep
// other workflow steps.
stepLines := []string{
" - name: " + stepName,
- " run: /opt/gh-aw/actions/install_copilot_cli.sh " + version,
+ " run: " + GhAwHome + "/actions/install_copilot_cli.sh " + version,
" env:",
" GH_HOST: github.com",
}
diff --git a/pkg/workflow/copilot_installer_test.go b/pkg/workflow/copilot_installer_test.go
index e9406cab4ab..83b7dadf482 100644
--- a/pkg/workflow/copilot_installer_test.go
+++ b/pkg/workflow/copilot_installer_test.go
@@ -24,7 +24,7 @@ func TestGenerateCopilotInstallerSteps(t *testing.T) {
stepName: "Install GitHub Copilot CLI",
expectedVersion: "0.0.369",
shouldContain: []string{
- "/opt/gh-aw/actions/install_copilot_cli.sh 0.0.369",
+ GhAwHome + "/actions/install_copilot_cli.sh 0.0.369",
"name: Install GitHub Copilot CLI",
"GH_HOST: github.com",
},
@@ -38,7 +38,7 @@ func TestGenerateCopilotInstallerSteps(t *testing.T) {
stepName: "Install GitHub Copilot CLI",
expectedVersion: "v0.0.370",
shouldContain: []string{
- "/opt/gh-aw/actions/install_copilot_cli.sh v0.0.370",
+ GhAwHome + "/actions/install_copilot_cli.sh v0.0.370",
"GH_HOST: github.com",
},
shouldNotContain: []string{
@@ -51,7 +51,7 @@ func TestGenerateCopilotInstallerSteps(t *testing.T) {
stepName: "Custom Install Step",
expectedVersion: "1.2.3",
shouldContain: []string{
- "/opt/gh-aw/actions/install_copilot_cli.sh 1.2.3",
+ GhAwHome + "/actions/install_copilot_cli.sh 1.2.3",
"name: Custom Install Step",
"GH_HOST: github.com",
},
@@ -65,7 +65,7 @@ func TestGenerateCopilotInstallerSteps(t *testing.T) {
stepName: "Install GitHub Copilot CLI",
expectedVersion: string(constants.DefaultCopilotVersion), // Should use DefaultCopilotVersion
shouldContain: []string{
- "/opt/gh-aw/actions/install_copilot_cli.sh " + string(constants.DefaultCopilotVersion),
+ GhAwHome + "/actions/install_copilot_cli.sh " + string(constants.DefaultCopilotVersion),
"GH_HOST: github.com",
},
shouldNotContain: []string{
@@ -100,7 +100,7 @@ func TestGenerateCopilotInstallerSteps(t *testing.T) {
}
// Verify the version is correctly passed to the install script
- expectedVersionLine := "/opt/gh-aw/actions/install_copilot_cli.sh " + tt.expectedVersion
+ expectedVersionLine := GhAwHome + "/actions/install_copilot_cli.sh " + tt.expectedVersion
if !strings.Contains(stepContent, expectedVersionLine) {
t.Errorf("Expected version to be set to '%s', but step content was:\n%s", tt.expectedVersion, stepContent)
}
@@ -137,7 +137,7 @@ func TestCopilotInstallerCustomVersion(t *testing.T) {
}
// Should contain the custom version
- expectedVersionLine := "/opt/gh-aw/actions/install_copilot_cli.sh " + customVersion
+ expectedVersionLine := GhAwHome + "/actions/install_copilot_cli.sh " + customVersion
if !strings.Contains(installStep, expectedVersionLine) {
t.Errorf("Expected custom version %s in install step, got:\n%s", customVersion, installStep)
}
diff --git a/pkg/workflow/copilot_mcp.go b/pkg/workflow/copilot_mcp.go
index e794944ba77..3501f594296 100644
--- a/pkg/workflow/copilot_mcp.go
+++ b/pkg/workflow/copilot_mcp.go
@@ -15,62 +15,18 @@ func (e *CopilotEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]
// Create the directory first
yaml.WriteString(" mkdir -p /home/runner/.copilot\n")
- // Create unified renderer with Copilot-specific options
// Copilot uses JSON format with type and tools fields, and inline args
- createRenderer := func(isLast bool) *MCPConfigRendererUnified {
- return NewMCPConfigRenderer(MCPRendererOptions{
- IncludeCopilotFields: true, // Copilot uses "type" and "tools" fields
- InlineArgs: true, // Copilot uses inline args format
- Format: "json",
- IsLast: isLast,
- ActionMode: GetActionModeFromWorkflowData(workflowData),
- WriteSinkGuardPolicies: deriveWriteSinkGuardPolicyFromWorkflow(workflowData),
- })
- }
+ createRenderer := buildMCPRendererFactory(workflowData, "json", true, true)
// Build gateway configuration for MCP config
// Per MCP Gateway Specification v1.0.0 section 4.1.3, the gateway section is required
- gatewayConfig := buildMCPGatewayConfig(workflowData)
-
- // Use shared JSON MCP config renderer with unified renderer methods
options := JSONMCPConfigOptions{
ConfigPath: "/home/runner/.copilot/mcp-config.json",
- GatewayConfig: gatewayConfig,
- Renderers: MCPToolRenderers{
- RenderGitHub: func(yaml *strings.Builder, githubTool any, isLast bool, workflowData *WorkflowData) {
- renderer := createRenderer(isLast)
- renderer.RenderGitHubMCP(yaml, githubTool, workflowData)
- },
- RenderPlaywright: func(yaml *strings.Builder, playwrightTool any, isLast bool) {
- renderer := createRenderer(isLast)
- renderer.RenderPlaywrightMCP(yaml, playwrightTool)
- },
- RenderSerena: func(yaml *strings.Builder, serenaTool any, isLast bool) {
- renderer := createRenderer(isLast)
- renderer.RenderSerenaMCP(yaml, serenaTool)
- },
- RenderCacheMemory: func(yaml *strings.Builder, isLast bool, workflowData *WorkflowData) {
- // Cache-memory is not used for Copilot (filtered out)
- },
- RenderAgenticWorkflows: func(yaml *strings.Builder, isLast bool) {
- renderer := createRenderer(isLast)
- renderer.RenderAgenticWorkflowsMCP(yaml)
- },
- RenderSafeOutputs: func(yaml *strings.Builder, isLast bool, workflowData *WorkflowData) {
- renderer := createRenderer(isLast)
- renderer.RenderSafeOutputsMCP(yaml, workflowData)
- },
- RenderMCPScripts: func(yaml *strings.Builder, mcpScripts *MCPScriptsConfig, isLast bool) {
- renderer := createRenderer(isLast)
- renderer.RenderMCPScriptsMCP(yaml, mcpScripts, workflowData)
- },
- RenderWebFetch: func(yaml *strings.Builder, isLast bool) {
- renderMCPFetchServerConfig(yaml, "json", " ", isLast, true, deriveWriteSinkGuardPolicyFromWorkflow(workflowData))
- },
- RenderCustomMCPConfig: func(yaml *strings.Builder, toolName string, toolConfig map[string]any, isLast bool) error {
- return e.renderCopilotMCPConfigWithContext(yaml, toolName, toolConfig, isLast, workflowData)
- },
- },
+ GatewayConfig: buildMCPGatewayConfig(workflowData),
+ // webFetchIncludeTools=true: Copilot requires a tools field in the web-fetch server config
+ Renderers: buildStandardJSONMCPRenderers(workflowData, createRenderer, true, func(yaml *strings.Builder, toolName string, toolConfig map[string]any, isLast bool) error {
+ return e.renderCopilotMCPConfigWithContext(yaml, toolName, toolConfig, isLast, workflowData)
+ }),
FilterTool: func(toolName string) bool {
// Filter out cache-memory for Copilot
// Cache-memory is handled as a simple file share, not an MCP server
diff --git a/pkg/workflow/data/ecosystem_domains.json b/pkg/workflow/data/ecosystem_domains.json
index 77edd24b499..1156e46181b 100644
--- a/pkg/workflow/data/ecosystem_domains.json
+++ b/pkg/workflow/data/ecosystem_domains.json
@@ -1,4 +1,8 @@
{
+ "bazel": ["releases.bazel.build", "mirror.bazel.build", "bcr.bazel.build", "blog.bazel.build"],
+ "clojure": ["repo.clojars.org", "clojars.org"],
+ "containers": ["ghcr.io", "registry.hub.docker.com", "*.docker.io", "*.docker.com", "production.cloudflare.docker.com", "dl.k8s.io", "pkgs.k8s.io", "quay.io", "mcr.microsoft.com", "gcr.io", "auth.docker.io"],
+ "dart": ["pub.dev", "pub.dartlang.org", "storage.googleapis.com"],
"defaults": [
"crl3.digicert.com",
"crl4.digicert.com",
@@ -35,7 +39,29 @@
"packages.cloud.google.com",
"packages.microsoft.com"
],
- "containers": ["ghcr.io", "registry.hub.docker.com", "*.docker.io", "*.docker.com", "production.cloudflare.docker.com", "dl.k8s.io", "pkgs.k8s.io", "quay.io", "mcr.microsoft.com", "gcr.io", "auth.docker.io"],
+ "dev-tools": [
+ "app.renovatebot.com",
+ "appveyor.com",
+ "badgen.net",
+ "circleci.com",
+ "codacy.com",
+ "codeclimate.com",
+ "codecov.io",
+ "coveralls.io",
+ "deepsource.io",
+ "drone.io",
+ "img.shields.io",
+ "readthedocs.io",
+ "readthedocs.org",
+ "renovatebot.com",
+ "semaphoreci.com",
+ "shields.io",
+ "snyk.io",
+ "sonarcloud.io",
+ "sonarqube.com",
+ "travis-ci.com"
+ ],
+ "local": ["127.0.0.1", "::1", "localhost"],
"dotnet": [
"nuget.org",
"dist.nuget.org",
@@ -55,26 +81,45 @@
"oneocsp.microsoft.com",
"*.vsblob.vsassets.io"
],
- "bazel": ["releases.bazel.build", "mirror.bazel.build", "bcr.bazel.build", "blog.bazel.build"],
- "clojure": ["repo.clojars.org", "clojars.org"],
- "dart": ["pub.dev", "pub.dartlang.org", "storage.googleapis.com"],
+ "elixir": ["hex.pm", "repo.hex.pm", "builds.hex.pm", "cdn.hex.pm", "fastly.hex.pm"],
"fonts": ["fonts.googleapis.com", "fonts.gstatic.com"],
"github": [
"*.githubusercontent.com",
- "raw.githubusercontent.com",
- "objects.githubusercontent.com",
- "lfs.github.com",
+ "codeload.github.com",
+ "docs.github.com",
"github-cloud.githubusercontent.com",
"github-cloud.s3.amazonaws.com",
- "codeload.github.com",
- "github.githubassets.com"
+ "github.blog",
+ "github.com",
+ "github.githubassets.com",
+ "lfs.github.com",
+ "objects.githubusercontent.com",
+ "raw.githubusercontent.com"
+ ],
+ "github-actions": [
+ "productionresultssa0.blob.core.windows.net",
+ "productionresultssa1.blob.core.windows.net",
+ "productionresultssa2.blob.core.windows.net",
+ "productionresultssa3.blob.core.windows.net",
+ "productionresultssa4.blob.core.windows.net",
+ "productionresultssa5.blob.core.windows.net",
+ "productionresultssa6.blob.core.windows.net",
+ "productionresultssa7.blob.core.windows.net",
+ "productionresultssa8.blob.core.windows.net",
+ "productionresultssa9.blob.core.windows.net",
+ "productionresultssa10.blob.core.windows.net",
+ "productionresultssa11.blob.core.windows.net",
+ "productionresultssa12.blob.core.windows.net",
+ "productionresultssa13.blob.core.windows.net",
+ "productionresultssa14.blob.core.windows.net",
+ "productionresultssa15.blob.core.windows.net",
+ "productionresultssa16.blob.core.windows.net",
+ "productionresultssa17.blob.core.windows.net",
+ "productionresultssa18.blob.core.windows.net",
+ "productionresultssa19.blob.core.windows.net"
],
- "elixir": ["hex.pm", "repo.hex.pm", "builds.hex.pm", "cdn.hex.pm", "fastly.hex.pm"],
"go": ["go.dev", "golang.org", "proxy.golang.org", "sum.golang.org", "pkg.go.dev", "goproxy.io", "storage.googleapis.com"],
- "terraform": ["releases.hashicorp.com", "apt.releases.hashicorp.com", "yum.releases.hashicorp.com", "registry.terraform.io"],
"haskell": ["haskell.org", "*.hackage.haskell.org", "get-ghcup.haskell.org", "downloads.haskell.org"],
- "kotlin": ["ge.jetbrains.com", "packages.jetbrains.team", "kotlin.bintray.com"],
- "julia": ["pkg.julialang.org", "*.pkg.julialang.org", "julialang.org", "julialang-s3.julialang.org", "storage.julialang.net"],
"java": [
"www.java.com",
"jdk.java.net",
@@ -105,6 +150,8 @@
"repo.gradle.org",
"downloads.gradle-dn.com"
],
+ "julia": ["pkg.julialang.org", "*.pkg.julialang.org", "julialang.org", "julialang-s3.julialang.org", "storage.julialang.net"],
+ "kotlin": ["ge.jetbrains.com", "packages.jetbrains.team", "kotlin.bintray.com"],
"linux-distros": [
"deb.debian.org",
"security.debian.org",
@@ -180,27 +227,6 @@
"rust": ["crates.io", "index.crates.io", "static.crates.io", "sh.rustup.rs", "static.rust-lang.org"],
"scala": ["repo.scala-sbt.org", "scala-ci.typesafe.com", "repo.typesafe.com", "jitpack.io", "dl.bintray.com"],
"swift": ["download.swift.org", "swift.org", "cocoapods.org", "cdn.cocoapods.org"],
- "zig": ["ziglang.org", "pkg.machengine.org"],
- "github-actions": [
- "productionresultssa0.blob.core.windows.net",
- "productionresultssa1.blob.core.windows.net",
- "productionresultssa2.blob.core.windows.net",
- "productionresultssa3.blob.core.windows.net",
- "productionresultssa4.blob.core.windows.net",
- "productionresultssa5.blob.core.windows.net",
- "productionresultssa6.blob.core.windows.net",
- "productionresultssa7.blob.core.windows.net",
- "productionresultssa8.blob.core.windows.net",
- "productionresultssa9.blob.core.windows.net",
- "productionresultssa10.blob.core.windows.net",
- "productionresultssa11.blob.core.windows.net",
- "productionresultssa12.blob.core.windows.net",
- "productionresultssa13.blob.core.windows.net",
- "productionresultssa14.blob.core.windows.net",
- "productionresultssa15.blob.core.windows.net",
- "productionresultssa16.blob.core.windows.net",
- "productionresultssa17.blob.core.windows.net",
- "productionresultssa18.blob.core.windows.net",
- "productionresultssa19.blob.core.windows.net"
- ]
+ "terraform": ["releases.hashicorp.com", "apt.releases.hashicorp.com", "yum.releases.hashicorp.com", "registry.terraform.io"],
+ "zig": ["ziglang.org", "pkg.machengine.org"]
}
diff --git a/pkg/workflow/detection_success_test.go b/pkg/workflow/detection_success_test.go
index b10ca9bc00c..4ad9631fa09 100644
--- a/pkg/workflow/detection_success_test.go
+++ b/pkg/workflow/detection_success_test.go
@@ -69,7 +69,7 @@ Create an issue.
}
// Check that the script uses require to load the parse_threat_detection_results.cjs file
- if !strings.Contains(agentSection, "require('/opt/gh-aw/actions/parse_threat_detection_results.cjs')") {
+ if !strings.Contains(agentSection, "require(process.env.GH_AW_HOME + '/actions/parse_threat_detection_results.cjs')") {
t.Error("Parse results step doesn't use require to load parse_threat_detection_results.cjs")
}
diff --git a/pkg/workflow/dispatch_workflow_validation.go b/pkg/workflow/dispatch_workflow_validation.go
index af07aaef572..87860fde37e 100644
--- a/pkg/workflow/dispatch_workflow_validation.go
+++ b/pkg/workflow/dispatch_workflow_validation.go
@@ -355,18 +355,5 @@ func extractMDWorkflowDispatchInputs(mdPath string) (map[string]any, error) {
// - []any: "on: [push, workflow_dispatch]"
// - map[string]any: "on:\n workflow_dispatch: ..."
func containsWorkflowDispatch(onSection any) bool {
- switch on := onSection.(type) {
- case string:
- return on == "workflow_dispatch"
- case []any:
- for _, trigger := range on {
- if triggerStr, ok := trigger.(string); ok && triggerStr == "workflow_dispatch" {
- return true
- }
- }
- case map[string]any:
- _, ok := on["workflow_dispatch"]
- return ok
- }
- return false
+ return containsTrigger(onSection, "workflow_dispatch")
}
diff --git a/pkg/workflow/docker.go b/pkg/workflow/docker.go
index 6a4339fb6e1..f8f7df08e48 100644
--- a/pkg/workflow/docker.go
+++ b/pkg/workflow/docker.go
@@ -184,7 +184,7 @@ func generateDownloadDockerImagesStep(yaml *strings.Builder, dockerImages []stri
}
yaml.WriteString(" - name: Download container images\n")
- yaml.WriteString(" run: bash /opt/gh-aw/actions/download_docker_images.sh")
+ yaml.WriteString(" run: bash " + GhAwHome + "/actions/download_docker_images.sh")
for _, image := range dockerImages {
fmt.Fprintf(yaml, " %s", image)
}
diff --git a/pkg/workflow/docker_predownload_test.go b/pkg/workflow/docker_predownload_test.go
index 1f5c223ff2a..a3f08de4e85 100644
--- a/pkg/workflow/docker_predownload_test.go
+++ b/pkg/workflow/docker_predownload_test.go
@@ -166,8 +166,8 @@ Test workflow with both GitHub and Serena tools.`,
// If we expect a step, verify the images are present
if tt.expectStep {
// Verify the script call is present
- if !strings.Contains(string(yaml), "bash /opt/gh-aw/actions/download_docker_images.sh") {
- t.Error("Expected to find 'bash /opt/gh-aw/actions/download_docker_images.sh' script call in generated YAML")
+ if !strings.Contains(string(yaml), "bash ${GH_AW_HOME}/actions/download_docker_images.sh") {
+ t.Error("Expected to find 'bash ${GH_AW_HOME}/actions/download_docker_images.sh' script call in generated YAML")
}
for _, expectedImage := range tt.expectedImages {
// Check that the image is being passed as an argument to the script
diff --git a/pkg/workflow/domains.go b/pkg/workflow/domains.go
index 634cdccbe7b..94895103bd1 100644
--- a/pkg/workflow/domains.go
+++ b/pkg/workflow/domains.go
@@ -128,9 +128,37 @@ func init() {
domainsLog.Printf("Loaded %d ecosystem categories", len(ecosystemDomains))
}
-// getEcosystemDomains returns the domains for a given ecosystem category
-// The returned list is sorted and contains unique entries
+// compoundEcosystems defines ecosystem identifiers that expand to the union of multiple
+// component ecosystems. These are resolved at lookup time, so they stay in sync with
+// any future changes to the component ecosystems.
+var compoundEcosystems = map[string][]string{
+ // default-safe-outputs: the recommended baseline for URL redaction in safe-outputs.
+ // Covers common infrastructure certificate/OCSP hosts (via "defaults"), popular
+ // developer-tool and CI/CD service domains (via "dev-tools"), GitHub domains (via "github"),
+ // and loopback/localhost addresses (via "local").
+ "default-safe-outputs": {"defaults", "dev-tools", "github", "local"},
+}
+
+// getEcosystemDomains returns the domains for a given ecosystem category.
+// Supports compound ecosystem identifiers (see compoundEcosystems).
+// The returned list is sorted and contains unique entries.
func getEcosystemDomains(category string) []string {
+ // Check for compound ecosystem first
+ if components, ok := compoundEcosystems[category]; ok {
+ domainMap := make(map[string]bool)
+ for _, component := range components {
+ for _, d := range getEcosystemDomains(component) {
+ domainMap[d] = true
+ }
+ }
+ result := make([]string, 0, len(domainMap))
+ for d := range domainMap {
+ result = append(result, d)
+ }
+ sort.Strings(result)
+ return result
+ }
+
domains, exists := ecosystemDomains[category]
if !exists {
return []string{}
@@ -313,6 +341,7 @@ var ecosystemPriority = []string{
"containers",
"dart",
"defaults",
+ "dev-tools",
"dotnet",
"elixir",
"fonts",
@@ -323,6 +352,7 @@ var ecosystemPriority = []string{
"java",
"kotlin",
"linux-distros",
+ "local",
"node",
"perl",
"php",
@@ -333,6 +363,7 @@ var ecosystemPriority = []string{
"swift",
"terraform",
"zig",
+ "default-safe-outputs", // compound: defaults + dev-tools + github + local
}
// GetDomainEcosystem returns the ecosystem identifier for a given domain, or empty string if not found.
@@ -611,6 +642,62 @@ func formatBlockedDomains(network *NetworkPermissions) string {
return strings.Join(blockedDomains, ",")
}
+// GetAPITargetDomains returns the set of domains to add to the allow-list when engine.api-target is set.
+// For a GHES instance with api-target "api.acme.ghe.com", this returns both the API domain
+// ("api.acme.ghe.com") and the base hostname ("acme.ghe.com") so that both the GitHub web UI
+// and API requests pass through the firewall without manual lock file edits.
+// Returns nil for empty apiTarget.
+func GetAPITargetDomains(apiTarget string) []string {
+ if apiTarget == "" {
+ return nil
+ }
+
+ domains := []string{apiTarget}
+
+ // Derive the base hostname by stripping the first subdomain label, but only for
+ // API-style hostnames that start with "api.".
+ // e.g., "api.acme.ghe.com" → "acme.ghe.com"
+ // Only add the base hostname if it still looks like a multi-label hostname (contains a dot).
+ if strings.HasPrefix(apiTarget, "api.") {
+ if idx := strings.Index(apiTarget, "."); idx > 0 {
+ baseHost := apiTarget[idx+1:]
+ if strings.Contains(baseHost, ".") && baseHost != apiTarget {
+ domains = append(domains, baseHost)
+ }
+ }
+ }
+
+ return domains
+}
+
+// mergeAPITargetDomains merges the api-target domains into an existing comma-separated domain string.
+// When engine.api-target is set, both the API hostname and its base hostname are added to the allow-list.
+// Returns the original string unchanged when apiTarget is empty.
+func mergeAPITargetDomains(domainsStr string, apiTarget string) string {
+ extraDomains := GetAPITargetDomains(apiTarget)
+ if len(extraDomains) == 0 {
+ return domainsStr
+ }
+
+ domainMap := make(map[string]bool)
+ for d := range strings.SplitSeq(domainsStr, ",") {
+ d = strings.TrimSpace(d)
+ if d != "" {
+ domainMap[d] = true
+ }
+ }
+ for _, d := range extraDomains {
+ domainMap[d] = true
+ }
+
+ result := make([]string, 0, len(domainMap))
+ for d := range domainMap {
+ result = append(result, d)
+ }
+ sort.Strings(result)
+ return strings.Join(result, ",")
+}
+
// computeAllowedDomainsForSanitization computes the allowed domains for sanitization
// based on the engine and network configuration, matching what's provided to the firewall
func (c *Compiler) computeAllowedDomainsForSanitization(data *WorkflowData) string {
@@ -624,18 +711,91 @@ func (c *Compiler) computeAllowedDomainsForSanitization(data *WorkflowData) stri
// Compute domains based on engine type, including tools and runtimes to match
// what's provided to the actual firewall at runtime
+ var base string
switch engineID {
case "copilot":
- return GetCopilotAllowedDomainsWithToolsAndRuntimes(data.NetworkPermissions, data.Tools, data.Runtimes)
+ base = GetCopilotAllowedDomainsWithToolsAndRuntimes(data.NetworkPermissions, data.Tools, data.Runtimes)
case "codex":
- return GetCodexAllowedDomainsWithToolsAndRuntimes(data.NetworkPermissions, data.Tools, data.Runtimes)
+ base = GetCodexAllowedDomainsWithToolsAndRuntimes(data.NetworkPermissions, data.Tools, data.Runtimes)
case "claude":
- return GetClaudeAllowedDomainsWithToolsAndRuntimes(data.NetworkPermissions, data.Tools, data.Runtimes)
+ base = GetClaudeAllowedDomainsWithToolsAndRuntimes(data.NetworkPermissions, data.Tools, data.Runtimes)
case "gemini":
- return GetGeminiAllowedDomainsWithToolsAndRuntimes(data.NetworkPermissions, data.Tools, data.Runtimes)
+ base = GetGeminiAllowedDomainsWithToolsAndRuntimes(data.NetworkPermissions, data.Tools, data.Runtimes)
default:
// For other engines, use network permissions only
domains := GetAllowedDomains(data.NetworkPermissions)
- return strings.Join(domains, ",")
+ base = strings.Join(domains, ",")
+ }
+
+ // Add GHES/custom API target domains so GH_AW_ALLOWED_DOMAINS stays in sync with --allow-domains
+ if data.EngineConfig != nil && data.EngineConfig.APITarget != "" {
+ base = mergeAPITargetDomains(base, data.EngineConfig.APITarget)
+ }
+
+ return base
+}
+
+// expandAllowedDomains expands a list of domain entries (which may include ecosystem
+// identifiers like "python", "node", "dev-tools") into a deduplicated, sorted list of
+// concrete domain strings. This uses the same expansion logic as network.allowed.
+func expandAllowedDomains(entries []string) []string {
+ domainMap := make(map[string]bool)
+ for _, entry := range entries {
+ ecosystemDomains := getEcosystemDomains(entry)
+ if len(ecosystemDomains) > 0 {
+ for _, d := range ecosystemDomains {
+ domainMap[d] = true
+ }
+ } else {
+ domainMap[entry] = true
+ }
+ }
+ result := make([]string, 0, len(domainMap))
+ for d := range domainMap {
+ result = append(result, d)
}
+ sort.Strings(result)
+ return result
+}
+
+// computeExpandedAllowedDomainsForSanitization computes the allowed domains for URL sanitization,
+// unioning the engine/network base set with the safe-outputs.allowed-domains entries.
+// It always includes "localhost" and "github.com" in the result.
+// The allowed-domains entries support ecosystem identifiers (same syntax as network.allowed).
+func (c *Compiler) computeExpandedAllowedDomainsForSanitization(data *WorkflowData) string {
+ // Start from the base set (engine defaults + network.allowed + tools + runtimes)
+ base := c.computeAllowedDomainsForSanitization(data)
+
+ domainMap := make(map[string]bool)
+
+ // Seed from the base computation
+ if base != "" {
+ for d := range strings.SplitSeq(base, ",") {
+ d = strings.TrimSpace(d)
+ if d != "" {
+ domainMap[d] = true
+ }
+ }
+ }
+
+ // Union with allowed-domains (expanded)
+ if data.SafeOutputs != nil && len(data.SafeOutputs.AllowedDomains) > 0 {
+ for _, d := range expandAllowedDomains(data.SafeOutputs.AllowedDomains) {
+ domainMap[d] = true
+ }
+ }
+
+ // Always allow localhost (for local development URL references)
+ domainMap["localhost"] = true
+
+ // Always allow github.com (GitHub page of the current repo)
+ domainMap["github.com"] = true
+
+ // Produce a sorted, comma-separated result
+ result := make([]string, 0, len(domainMap))
+ for d := range domainMap {
+ result = append(result, d)
+ }
+ sort.Strings(result)
+ return strings.Join(result, ",")
}
diff --git a/pkg/workflow/domains_test.go b/pkg/workflow/domains_test.go
index 0d3cf98c84b..aa2a024fa85 100644
--- a/pkg/workflow/domains_test.go
+++ b/pkg/workflow/domains_test.go
@@ -892,3 +892,271 @@ func TestGetCodexAllowedDomainsWithToolsAndRuntimes(t *testing.T) {
}
})
}
+
+// TestExpandAllowedDomains tests the expandAllowedDomains function
+func TestExpandAllowedDomains(t *testing.T) {
+ t.Run("plain domains are returned as-is", func(t *testing.T) {
+ result := expandAllowedDomains([]string{"example.com", "test.org"})
+ if !strings.Contains(strings.Join(result, ","), "example.com") {
+ t.Error("Expected example.com in result")
+ }
+ if !strings.Contains(strings.Join(result, ","), "test.org") {
+ t.Error("Expected test.org in result")
+ }
+ })
+
+ t.Run("ecosystem identifiers are expanded", func(t *testing.T) {
+ result := expandAllowedDomains([]string{"python"})
+ joined := strings.Join(result, ",")
+ if !strings.Contains(joined, "pypi.org") {
+ t.Error("Expected pypi.org from python ecosystem in result")
+ }
+ })
+
+ t.Run("dev-tools ecosystem is expanded", func(t *testing.T) {
+ result := expandAllowedDomains([]string{"dev-tools"})
+ joined := strings.Join(result, ",")
+ if !strings.Contains(joined, "codecov.io") {
+ t.Error("Expected codecov.io from dev-tools ecosystem in result")
+ }
+ if !strings.Contains(joined, "snyk.io") {
+ t.Error("Expected snyk.io from dev-tools ecosystem in result")
+ }
+ })
+
+ t.Run("mixed plain domains and ecosystem identifiers", func(t *testing.T) {
+ result := expandAllowedDomains([]string{"example.com", "python"})
+ joined := strings.Join(result, ",")
+ if !strings.Contains(joined, "example.com") {
+ t.Error("Expected example.com in result")
+ }
+ if !strings.Contains(joined, "pypi.org") {
+ t.Error("Expected pypi.org from python ecosystem in result")
+ }
+ })
+
+ t.Run("empty input returns empty result", func(t *testing.T) {
+ result := expandAllowedDomains([]string{})
+ if len(result) != 0 {
+ t.Errorf("Expected empty result, got %v", result)
+ }
+ })
+}
+
+// TestComputeExpandedAllowedDomainsForSanitization tests that allowed-domains are unioned with
+// the engine/network base set and always includes localhost and github.com
+func TestComputeExpandedAllowedDomainsForSanitization(t *testing.T) {
+ compiler := NewCompiler()
+
+ t.Run("unions with engine base set", func(t *testing.T) {
+ data := &WorkflowData{
+ EngineConfig: &EngineConfig{ID: "copilot"},
+ NetworkPermissions: &NetworkPermissions{
+ Allowed: []string{"example.com"},
+ },
+ SafeOutputs: &SafeOutputsConfig{
+ AllowedDomains: []string{"extra-domain.com"},
+ },
+ }
+ result := compiler.computeExpandedAllowedDomainsForSanitization(data)
+ if !strings.Contains(result, "extra-domain.com") {
+ t.Error("Expected extra-domain.com in result")
+ }
+ if !strings.Contains(result, "example.com") {
+ t.Errorf("Expected network domain example.com in result, got: %s", result)
+ }
+ if !strings.Contains(result, "api.github.com") {
+ t.Error("Expected Copilot default api.github.com in result")
+ }
+ })
+
+ t.Run("always includes localhost", func(t *testing.T) {
+ data := &WorkflowData{
+ EngineConfig: &EngineConfig{ID: "copilot"},
+ SafeOutputs: &SafeOutputsConfig{
+ AllowedDomains: []string{"extra-domain.com"},
+ },
+ }
+ result := compiler.computeExpandedAllowedDomainsForSanitization(data)
+ if !strings.Contains(result, "localhost") {
+ t.Error("Expected localhost to always be in allowed-domains result")
+ }
+ })
+
+ t.Run("always includes github.com", func(t *testing.T) {
+ data := &WorkflowData{
+ EngineConfig: &EngineConfig{ID: "codex"},
+ SafeOutputs: &SafeOutputsConfig{
+ AllowedDomains: []string{"extra-domain.com"},
+ },
+ }
+ result := compiler.computeExpandedAllowedDomainsForSanitization(data)
+ if !strings.Contains(result, "github.com") {
+ t.Error("Expected github.com to always be in allowed-domains result")
+ }
+ })
+
+ t.Run("supports ecosystem identifiers", func(t *testing.T) {
+ data := &WorkflowData{
+ EngineConfig: &EngineConfig{ID: "copilot"},
+ SafeOutputs: &SafeOutputsConfig{
+ AllowedDomains: []string{"python", "dev-tools"},
+ },
+ }
+ result := compiler.computeExpandedAllowedDomainsForSanitization(data)
+ if !strings.Contains(result, "pypi.org") {
+ t.Error("Expected pypi.org from python ecosystem in result")
+ }
+ if !strings.Contains(result, "codecov.io") {
+ t.Error("Expected codecov.io from dev-tools ecosystem in result")
+ }
+ })
+}
+
+// TestDefaultSafeOutputsEcosystem tests that the default-safe-outputs compound ecosystem
+// correctly expands to the union of defaults + dev-tools + github + local
+func TestDefaultSafeOutputsEcosystem(t *testing.T) {
+ result := expandAllowedDomains([]string{"default-safe-outputs"})
+ joined := strings.Join(result, ",")
+
+ // From defaults ecosystem
+ defaultsSamples := []string{"json-schema.org", "archive.ubuntu.com", "ocsp.digicert.com"}
+ // From dev-tools ecosystem
+ devToolsSamples := []string{"codecov.io", "snyk.io", "shields.io"}
+ // From github ecosystem
+ githubSamples := []string{"github.com", "docs.github.com", "github.blog", "*.githubusercontent.com"}
+ // From local ecosystem
+ localSamples := []string{"localhost", "127.0.0.1", "::1"}
+
+ for _, d := range append(append(append(defaultsSamples, devToolsSamples...), githubSamples...), localSamples...) {
+ if !strings.Contains(joined, d) {
+ t.Errorf("Expected domain %q from default-safe-outputs ecosystem in result", d)
+ }
+ }
+
+ // Verify the size matches the union of all component ecosystems
+ expectedDomains := make(map[string]bool)
+ for _, component := range []string{"defaults", "dev-tools", "github", "local"} {
+ for _, d := range getEcosystemDomains(component) {
+ expectedDomains[d] = true
+ }
+ }
+ if len(result) != len(expectedDomains) {
+ t.Errorf("Expected %d unique domains in default-safe-outputs (union of components), got %d", len(expectedDomains), len(result))
+ }
+}
+
+func TestGetAPITargetDomains(t *testing.T) {
+ tests := []struct {
+ name string
+ apiTarget string
+ expected []string
+ }{
+ {
+ name: "empty api-target returns nil",
+ apiTarget: "",
+ expected: nil,
+ },
+ {
+ name: "GHES api-target with api. prefix returns both api and base domains",
+ apiTarget: "api.acme.ghe.com",
+ expected: []string{"api.acme.ghe.com", "acme.ghe.com"},
+ },
+ {
+ name: "GHES api-target custom domain",
+ apiTarget: "api.contoso-aw.ghe.com",
+ expected: []string{"api.contoso-aw.ghe.com", "contoso-aw.ghe.com"},
+ },
+ {
+ name: "enterprise githubcopilot.com api-target",
+ apiTarget: "api.enterprise.githubcopilot.com",
+ expected: []string{"api.enterprise.githubcopilot.com", "enterprise.githubcopilot.com"},
+ },
+ {
+ name: "non-api. prefix hostname returns only itself",
+ apiTarget: "copilot.example.com",
+ expected: []string{"copilot.example.com"},
+ },
+ {
+ name: "single label hostname (no dot) returns only itself",
+ apiTarget: "localhost",
+ expected: []string{"localhost"},
+ },
+ {
+ name: "two-label hostname does not add TLD alone",
+ apiTarget: "example.com",
+ expected: []string{"example.com"},
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := GetAPITargetDomains(tt.apiTarget)
+ if tt.expected == nil {
+ if result != nil {
+ t.Errorf("Expected nil, got %v", result)
+ }
+ return
+ }
+ if len(result) != len(tt.expected) {
+ t.Errorf("Expected %d domains %v, got %d domains %v", len(tt.expected), tt.expected, len(result), result)
+ return
+ }
+ for _, expected := range tt.expected {
+ if !slices.Contains(result, expected) {
+ t.Errorf("Expected domain %q not found in result %v", expected, result)
+ }
+ }
+ })
+ }
+}
+
+func TestMergeAPITargetDomains(t *testing.T) {
+ tests := []struct {
+ name string
+ domainsStr string
+ apiTarget string
+ wantIn []string
+ wantNotIn []string
+ }{
+ {
+ name: "empty api-target leaves domains unchanged",
+ domainsStr: "github.com,api.github.com",
+ apiTarget: "",
+ wantIn: []string{"github.com", "api.github.com"},
+ },
+ {
+ name: "GHES api-target adds both api and base domains",
+ domainsStr: "github.com,api.github.com",
+ apiTarget: "api.acme.ghe.com",
+ wantIn: []string{"github.com", "api.github.com", "api.acme.ghe.com", "acme.ghe.com"},
+ },
+ {
+ name: "result is sorted and deduplicated",
+ domainsStr: "api.acme.ghe.com,github.com",
+ apiTarget: "api.acme.ghe.com",
+ wantIn: []string{"api.acme.ghe.com", "acme.ghe.com", "github.com"},
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := mergeAPITargetDomains(tt.domainsStr, tt.apiTarget)
+ domains := strings.Split(result, ",")
+ domainSet := make(map[string]bool)
+ for _, d := range domains {
+ domainSet[d] = true
+ }
+ for _, want := range tt.wantIn {
+ if !domainSet[want] {
+ t.Errorf("Expected domain %q in result %q, but not found", want, result)
+ }
+ }
+ for _, notWant := range tt.wantNotIn {
+ if domainSet[notWant] {
+ t.Errorf("Did not expect domain %q in result %q", notWant, result)
+ }
+ }
+ })
+ }
+}
diff --git a/pkg/workflow/engine_helpers_shared_test.go b/pkg/workflow/engine_helpers_shared_test.go
index 09d58de7e16..ab687f24ebc 100644
--- a/pkg/workflow/engine_helpers_shared_test.go
+++ b/pkg/workflow/engine_helpers_shared_test.go
@@ -389,7 +389,7 @@ func TestRenderJSONMCPConfig(t *testing.T) {
},
},
expectedContent: []string{
- "cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh",
+ "cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh",
"\"mcpServers\": {",
"\"github\": { \"test\": true },",
"\"playwright\": { \"test\": true }",
@@ -426,7 +426,7 @@ func TestRenderJSONMCPConfig(t *testing.T) {
},
},
expectedContent: []string{
- "cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh",
+ "cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh",
"\"github\": { \"filtered\": true }",
},
unexpectedContent: []string{
diff --git a/pkg/workflow/engine_includes_test.go b/pkg/workflow/engine_includes_test.go
index 543b74b339e..17fea18eca7 100644
--- a/pkg/workflow/engine_includes_test.go
+++ b/pkg/workflow/engine_includes_test.go
@@ -253,7 +253,7 @@ This should use the default engine.
lockStr := string(lockContent)
// Should contain references to copilot CLI (default engine) using install script wrapper
- if !strings.Contains(lockStr, "/opt/gh-aw/actions/install_copilot_cli.sh") {
+ if !strings.Contains(lockStr, "${GH_AW_HOME}/actions/install_copilot_cli.sh") {
t.Error("Expected lock file to contain copilot CLI installation using install script wrapper")
}
diff --git a/pkg/workflow/firewall_version_pinning_test.go b/pkg/workflow/firewall_version_pinning_test.go
index e5a0539d67f..4a40249e42d 100644
--- a/pkg/workflow/firewall_version_pinning_test.go
+++ b/pkg/workflow/firewall_version_pinning_test.go
@@ -27,9 +27,9 @@ func TestAWFInstallationStepDefaultVersion(t *testing.T) {
t.Error("Expected to call install_awf_binary.sh script")
}
- // Verify it uses the script from /opt/gh-aw/actions/
- if !strings.Contains(stepStr, "/opt/gh-aw/actions/install_awf_binary.sh") {
- t.Error("Expected to call script from /opt/gh-aw/actions/ directory")
+ // Verify it uses the script from /actions/
+ if !strings.Contains(stepStr, "${GH_AW_HOME}/actions/install_awf_binary.sh") {
+ t.Error("Expected to call script from /actions/ directory")
}
// Ensure it's NOT using inline bash or the old unverified installer script
diff --git a/pkg/workflow/frontmatter_extraction_metadata.go b/pkg/workflow/frontmatter_extraction_metadata.go
index 944e263e157..9e395af4438 100644
--- a/pkg/workflow/frontmatter_extraction_metadata.go
+++ b/pkg/workflow/frontmatter_extraction_metadata.go
@@ -3,7 +3,6 @@ package workflow
import (
"fmt"
"maps"
- "math"
"strings"
"github.com/github/gh-aw/pkg/logger"
@@ -144,20 +143,7 @@ func buildSourceURL(source string) string {
}
// safeUintToInt safely converts uint to int, returning 0 if overflow would occur
-func safeUintToInt(u uint) int {
- if u > math.MaxInt {
- return 0 // Return 0 (engine default) if value would overflow
- }
- return int(u)
-}
-
-// safeUint64ToInt safely converts uint64 to int, returning 0 if overflow would occur
-func safeUint64ToInt(u uint64) int {
- if u > math.MaxInt {
- return 0 // Return 0 (engine default) if value would overflow
- }
- return int(u)
-}
+func safeUintToInt(u uint) int { return safeUint64ToInt(uint64(u)) }
// extractToolsTimeout extracts the timeout setting from tools
// Returns 0 if not set (engines will use their own defaults)
@@ -362,7 +348,7 @@ func extractPluginsFromFrontmatter(frontmatter map[string]any) *PluginInfo {
// extractAPMDependenciesFromFrontmatter extracts APM (Agent Package Manager) dependency
// configuration from frontmatter. Supports two formats:
// - Array format: ["org/pkg1", "org/pkg2"]
-// - Object format: {packages: ["org/pkg1", "org/pkg2"], isolated: true}
+// - Object format: {packages: ["org/pkg1", "org/pkg2"], isolated: true, github-app: {...}, version: "v0.8.0"}
//
// Returns nil if no dependencies field is present or if the field contains no packages.
func extractAPMDependenciesFromFrontmatter(frontmatter map[string]any) *APMDependenciesInfo {
@@ -373,6 +359,8 @@ func extractAPMDependenciesFromFrontmatter(frontmatter map[string]any) *APMDepen
var packages []string
var isolated bool
+ var githubApp *GitHubAppConfig
+ var version string
switch v := value.(type) {
case []any:
@@ -383,7 +371,7 @@ func extractAPMDependenciesFromFrontmatter(frontmatter map[string]any) *APMDepen
}
}
case map[string]any:
- // Object format: dependencies: {packages: [...], isolated: true}
+ // Object format: dependencies: {packages: [...], isolated: true, github-app: {...}, version: "v0.8.0"}
if pkgsAny, ok := v["packages"]; ok {
if pkgsArray, ok := pkgsAny.([]any); ok {
for _, item := range pkgsArray {
@@ -398,6 +386,20 @@ func extractAPMDependenciesFromFrontmatter(frontmatter map[string]any) *APMDepen
isolated = isoBool
}
}
+ if appAny, ok := v["github-app"]; ok {
+ if appMap, ok := appAny.(map[string]any); ok {
+ githubApp = parseAppConfig(appMap)
+ if githubApp.AppID == "" || githubApp.PrivateKey == "" {
+ frontmatterMetadataLog.Print("dependencies.github-app missing required app-id or private-key; ignoring")
+ githubApp = nil
+ }
+ }
+ }
+ if versionAny, ok := v["version"]; ok {
+ if versionStr, ok := versionAny.(string); ok && versionStr != "" {
+ version = versionStr
+ }
+ }
default:
return nil
}
@@ -406,6 +408,6 @@ func extractAPMDependenciesFromFrontmatter(frontmatter map[string]any) *APMDepen
return nil
}
- frontmatterMetadataLog.Printf("Extracted %d APM dependency packages from frontmatter (isolated=%v)", len(packages), isolated)
- return &APMDependenciesInfo{Packages: packages, Isolated: isolated}
+ frontmatterMetadataLog.Printf("Extracted %d APM dependency packages from frontmatter (isolated=%v, github-app=%v, version=%s)", len(packages), isolated, githubApp != nil, version)
+ return &APMDependenciesInfo{Packages: packages, Isolated: isolated, GitHubApp: githubApp, Version: version}
}
diff --git a/pkg/workflow/frontmatter_extraction_security.go b/pkg/workflow/frontmatter_extraction_security.go
index 2f75133c9c0..183c8d52595 100644
--- a/pkg/workflow/frontmatter_extraction_security.go
+++ b/pkg/workflow/frontmatter_extraction_security.go
@@ -438,6 +438,47 @@ func (c *Compiler) extractMCPGatewayConfig(mcpVal any) *MCPGatewayRuntimeConfig
}
}
+ // Extract payloadDir / payload-dir (directory for storing large payload JSON files)
+ for _, key := range []string{"payloadDir", "payload-dir"} {
+ if payloadDirVal, hasPayloadDir := mcpObj[key]; hasPayloadDir {
+ if payloadDirStr, ok := payloadDirVal.(string); ok {
+ mcpConfig.PayloadDir = payloadDirStr
+ break
+ }
+ }
+ }
+
+ // Extract payloadPathPrefix / payload-path-prefix (path prefix to remap payload paths)
+ for _, key := range []string{"payloadPathPrefix", "payload-path-prefix"} {
+ if payloadPathPrefixVal, hasPayloadPathPrefix := mcpObj[key]; hasPayloadPathPrefix {
+ if payloadPathPrefixStr, ok := payloadPathPrefixVal.(string); ok {
+ mcpConfig.PayloadPathPrefix = payloadPathPrefixStr
+ break
+ }
+ }
+ }
+
+ // Extract payloadSizeThreshold / payload-size-threshold (size threshold in bytes)
+ for _, key := range []string{"payloadSizeThreshold", "payload-size-threshold"} {
+ if payloadSizeThresholdVal, hasPayloadSizeThreshold := mcpObj[key]; hasPayloadSizeThreshold {
+ switch v := payloadSizeThresholdVal.(type) {
+ case int:
+ mcpConfig.PayloadSizeThreshold = v
+ case int64:
+ mcpConfig.PayloadSizeThreshold = int(v)
+ case uint:
+ mcpConfig.PayloadSizeThreshold = int(v)
+ case uint64:
+ mcpConfig.PayloadSizeThreshold = int(v)
+ case float64:
+ mcpConfig.PayloadSizeThreshold = int(v)
+ }
+ if mcpConfig.PayloadSizeThreshold != 0 {
+ break
+ }
+ }
+ }
+
return mcpConfig
}
diff --git a/pkg/workflow/frontmatter_extraction_security_test.go b/pkg/workflow/frontmatter_extraction_security_test.go
index 69804d74c07..761afbfdf01 100644
--- a/pkg/workflow/frontmatter_extraction_security_test.go
+++ b/pkg/workflow/frontmatter_extraction_security_test.go
@@ -117,3 +117,104 @@ func TestExtractFirewallConfig(t *testing.T) {
assert.Nil(t, config.AllowURLs, "Should ignore non-array allow-urls value")
})
}
+
+// TestExtractMCPGatewayConfigPayloadFields tests extraction of payload-related fields
+// from MCP gateway frontmatter configuration
+func TestExtractMCPGatewayConfigPayloadFields(t *testing.T) {
+ compiler := &Compiler{}
+
+ t.Run("extracts payloadDir using camelCase key", func(t *testing.T) {
+ mcpObj := map[string]any{
+ "container": "ghcr.io/github/gh-aw-mcpg",
+ "payloadDir": "/custom/payloads",
+ }
+ config := compiler.extractMCPGatewayConfig(mcpObj)
+ require.NotNil(t, config, "Should extract MCP gateway config")
+ assert.Equal(t, "/custom/payloads", config.PayloadDir, "Should extract payloadDir")
+ })
+
+ t.Run("extracts payloadDir using kebab-case key", func(t *testing.T) {
+ mcpObj := map[string]any{
+ "container": "ghcr.io/github/gh-aw-mcpg",
+ "payload-dir": "/custom/payloads",
+ }
+ config := compiler.extractMCPGatewayConfig(mcpObj)
+ require.NotNil(t, config, "Should extract MCP gateway config")
+ assert.Equal(t, "/custom/payloads", config.PayloadDir, "Should extract payload-dir")
+ })
+
+ t.Run("extracts payloadPathPrefix using camelCase key", func(t *testing.T) {
+ mcpObj := map[string]any{
+ "container": "ghcr.io/github/gh-aw-mcpg",
+ "payloadPathPrefix": "/workspace/payloads",
+ }
+ config := compiler.extractMCPGatewayConfig(mcpObj)
+ require.NotNil(t, config, "Should extract MCP gateway config")
+ assert.Equal(t, "/workspace/payloads", config.PayloadPathPrefix, "Should extract payloadPathPrefix")
+ })
+
+ t.Run("extracts payloadPathPrefix using kebab-case key", func(t *testing.T) {
+ mcpObj := map[string]any{
+ "container": "ghcr.io/github/gh-aw-mcpg",
+ "payload-path-prefix": "/workspace/payloads",
+ }
+ config := compiler.extractMCPGatewayConfig(mcpObj)
+ require.NotNil(t, config, "Should extract MCP gateway config")
+ assert.Equal(t, "/workspace/payloads", config.PayloadPathPrefix, "Should extract payload-path-prefix")
+ })
+
+ t.Run("extracts payloadSizeThreshold using camelCase key", func(t *testing.T) {
+ mcpObj := map[string]any{
+ "container": "ghcr.io/github/gh-aw-mcpg",
+ "payloadSizeThreshold": 65536,
+ }
+ config := compiler.extractMCPGatewayConfig(mcpObj)
+ require.NotNil(t, config, "Should extract MCP gateway config")
+ assert.Equal(t, 65536, config.PayloadSizeThreshold, "Should extract payloadSizeThreshold")
+ })
+
+ t.Run("extracts payloadSizeThreshold using kebab-case key", func(t *testing.T) {
+ mcpObj := map[string]any{
+ "container": "ghcr.io/github/gh-aw-mcpg",
+ "payload-size-threshold": 65536,
+ }
+ config := compiler.extractMCPGatewayConfig(mcpObj)
+ require.NotNil(t, config, "Should extract MCP gateway config")
+ assert.Equal(t, 65536, config.PayloadSizeThreshold, "Should extract payload-size-threshold")
+ })
+
+ t.Run("extracts payloadSizeThreshold as float64 (YAML default numeric type)", func(t *testing.T) {
+ mcpObj := map[string]any{
+ "container": "ghcr.io/github/gh-aw-mcpg",
+ "payloadSizeThreshold": float64(65536),
+ }
+ config := compiler.extractMCPGatewayConfig(mcpObj)
+ require.NotNil(t, config, "Should extract MCP gateway config")
+ assert.Equal(t, 65536, config.PayloadSizeThreshold, "Should extract payloadSizeThreshold from float64")
+ })
+
+ t.Run("extracts all payload fields together", func(t *testing.T) {
+ mcpObj := map[string]any{
+ "container": "ghcr.io/github/gh-aw-mcpg",
+ "payloadDir": "/custom/payloads",
+ "payloadPathPrefix": "/workspace/payloads",
+ "payloadSizeThreshold": 1048576,
+ }
+ config := compiler.extractMCPGatewayConfig(mcpObj)
+ require.NotNil(t, config, "Should extract MCP gateway config")
+ assert.Equal(t, "/custom/payloads", config.PayloadDir, "Should extract payloadDir")
+ assert.Equal(t, "/workspace/payloads", config.PayloadPathPrefix, "Should extract payloadPathPrefix")
+ assert.Equal(t, 1048576, config.PayloadSizeThreshold, "Should extract payloadSizeThreshold")
+ })
+
+ t.Run("leaves payload fields zero/empty when not specified", func(t *testing.T) {
+ mcpObj := map[string]any{
+ "container": "ghcr.io/github/gh-aw-mcpg",
+ }
+ config := compiler.extractMCPGatewayConfig(mcpObj)
+ require.NotNil(t, config, "Should extract MCP gateway config")
+ assert.Empty(t, config.PayloadDir, "PayloadDir should be empty when not specified")
+ assert.Empty(t, config.PayloadPathPrefix, "PayloadPathPrefix should be empty when not specified")
+ assert.Equal(t, 0, config.PayloadSizeThreshold, "PayloadSizeThreshold should be 0 when not specified")
+ })
+}
diff --git a/pkg/workflow/frontmatter_extraction_yaml.go b/pkg/workflow/frontmatter_extraction_yaml.go
index f129087d0b7..4c774f827df 100644
--- a/pkg/workflow/frontmatter_extraction_yaml.go
+++ b/pkg/workflow/frontmatter_extraction_yaml.go
@@ -100,7 +100,7 @@ func (c *Compiler) extractTopLevelYAMLSection(frontmatter map[string]any, key st
return yamlStr
}
-// commentOutProcessedFieldsInOnSection comments out draft, fork, forks, names, manual-approval, stop-after, skip-if-match, skip-if-no-match, skip-roles, reaction, and lock-for-agent fields in the on section
+// commentOutProcessedFieldsInOnSection comments out draft, fork, forks, names, manual-approval, stop-after, skip-if-match, skip-if-no-match, skip-roles, reaction, lock-for-agent, steps, and permissions fields in the on section
// These fields are processed separately and should be commented for documentation
// Exception: names fields in sections with __gh_aw_native_label_filter__ marker in frontmatter are NOT commented out
func (c *Compiler) commentOutProcessedFieldsInOnSection(yamlStr string, frontmatter map[string]any) string {
@@ -139,45 +139,53 @@ func (c *Compiler) commentOutProcessedFieldsInOnSection(yamlStr string, frontmat
inRolesArray := false
inBotsArray := false
inGitHubApp := false
+ inOnSteps := false
+ inOnPermissions := false
currentSection := "" // Track which section we're in ("issues", "pull_request", "discussion", or "issue_comment")
for _, line := range lines {
- // Check if we're entering a pull_request, issues, discussion, or issue_comment section
- if strings.Contains(line, "pull_request:") {
- inPullRequest = true
- inIssues = false
- inDiscussion = false
- inIssueComment = false
- currentSection = "pull_request"
- result = append(result, line)
- continue
- }
- if strings.Contains(line, "issues:") {
- inIssues = true
- inPullRequest = false
- inDiscussion = false
- inIssueComment = false
- currentSection = "issues"
- result = append(result, line)
- continue
- }
- if strings.Contains(line, "discussion:") {
- inDiscussion = true
- inPullRequest = false
- inIssues = false
- inIssueComment = false
- currentSection = "discussion"
- result = append(result, line)
- continue
- }
- if strings.Contains(line, "issue_comment:") {
- inIssueComment = true
- inPullRequest = false
- inIssues = false
- inDiscussion = false
- currentSection = "issue_comment"
- result = append(result, line)
- continue
+ // Check if we're entering a pull_request, issues, discussion, or issue_comment section.
+ // Skip these checks when inside on.permissions or on.steps to avoid false matches.
+ // Example: ` issues: read` inside on.permissions was previously matched as the
+ // `issues:` event trigger, incorrectly entering the inIssues state and suppressing
+ // the permission comment-out logic.
+ if !inOnPermissions && !inOnSteps {
+ if strings.Contains(line, "pull_request:") {
+ inPullRequest = true
+ inIssues = false
+ inDiscussion = false
+ inIssueComment = false
+ currentSection = "pull_request"
+ result = append(result, line)
+ continue
+ }
+ if strings.Contains(line, "issues:") {
+ inIssues = true
+ inPullRequest = false
+ inDiscussion = false
+ inIssueComment = false
+ currentSection = "issues"
+ result = append(result, line)
+ continue
+ }
+ if strings.Contains(line, "discussion:") {
+ inDiscussion = true
+ inPullRequest = false
+ inIssues = false
+ inIssueComment = false
+ currentSection = "discussion"
+ result = append(result, line)
+ continue
+ }
+ if strings.Contains(line, "issue_comment:") {
+ inIssueComment = true
+ inPullRequest = false
+ inIssues = false
+ inDiscussion = false
+ currentSection = "issue_comment"
+ result = append(result, line)
+ continue
+ }
}
// Check if we're leaving the pull_request, issues, discussion, or issue_comment section (new top-level key or end of indent)
@@ -232,6 +240,17 @@ func (c *Compiler) commentOutProcessedFieldsInOnSection(yamlStr string, frontmat
inBotsArray = true
}
+ // Check if we're entering on.steps array
+ if !inPullRequest && !inIssues && !inDiscussion && !inIssueComment && strings.HasPrefix(trimmedLine, "steps:") {
+ inOnSteps = true
+ }
+
+ // Check if we're entering on.permissions object
+ if !inPullRequest && !inIssues && !inDiscussion && !inIssueComment && !inOnPermissions &&
+ strings.HasPrefix(trimmedLine, "permissions:") {
+ inOnPermissions = true
+ }
+
// Check if we're entering skip-if-match object
if !inPullRequest && !inIssues && !inDiscussion && !inIssueComment && !inSkipIfMatch {
// Check both uncommented and commented forms
@@ -353,6 +372,25 @@ func (c *Compiler) commentOutProcessedFieldsInOnSection(yamlStr string, frontmat
}
}
+ // Check if we're leaving the on.steps array by encountering another top-level field
+ if inOnSteps && strings.TrimSpace(line) != "" {
+ lineIndent := len(line) - len(strings.TrimLeft(line, " \t"))
+ // If this is a line at the same level as steps (2 spaces) and not a dash or comment, we're out
+ if lineIndent == 2 && !strings.HasPrefix(trimmedLine, "-") && !strings.HasPrefix(trimmedLine, "steps:") && !strings.HasPrefix(trimmedLine, "#") {
+ inOnSteps = false
+ }
+ }
+
+ // Check if we're leaving the on.permissions object by encountering another top-level field
+ if inOnPermissions && strings.TrimSpace(line) != "" &&
+ !strings.HasPrefix(trimmedLine, "permissions:") &&
+ !strings.HasPrefix(trimmedLine, "# permissions:") {
+ lineIndent := len(line) - len(strings.TrimLeft(line, " \t"))
+ if lineIndent == 2 && !strings.HasPrefix(trimmedLine, "#") {
+ inOnPermissions = false
+ }
+ }
+
// Determine if we should comment out this line
shouldComment := false
var commentReason string
@@ -407,6 +445,20 @@ func (c *Compiler) commentOutProcessedFieldsInOnSection(yamlStr string, frontmat
// Comment out array items in bots
shouldComment = true
commentReason = " # Bots processed as bot check in pre-activation job"
+ } else if strings.HasPrefix(trimmedLine, "steps:") {
+ shouldComment = true
+ commentReason = " # Steps injected into pre-activation job"
+ } else if inOnSteps {
+ // Comment out all content of on.steps (both array items and their nested fields)
+ shouldComment = true
+ commentReason = ""
+ } else if strings.HasPrefix(trimmedLine, "permissions:") {
+ shouldComment = true
+ commentReason = " # Permissions applied to pre-activation job"
+ } else if inOnPermissions {
+ // Comment out all nested permission scope lines
+ shouldComment = true
+ commentReason = ""
} else if strings.HasPrefix(trimmedLine, "reaction:") {
shouldComment = true
commentReason = " # Reaction processed as activation job step"
@@ -690,6 +742,73 @@ func (c *Compiler) extractCommandConfig(frontmatter map[string]any) (commandName
return nil, nil
}
+// extractLabelCommandConfig extracts the label-command configuration from frontmatter
+// including label name(s) and the events field.
+// It reads on.label_command which can be:
+// - a string: label name directly (e.g. label_command: "deploy")
+// - a map with "name" or "names" and optional "events" fields
+//
+// Returns (labelNames, labelEvents) where labelEvents is nil for default (all events).
+func (c *Compiler) extractLabelCommandConfig(frontmatter map[string]any) (labelNames []string, labelEvents []string) {
+ frontmatterLog.Print("Extracting label-command configuration from frontmatter")
+ onValue, exists := frontmatter["on"]
+ if !exists {
+ return nil, nil
+ }
+ onMap, ok := onValue.(map[string]any)
+ if !ok {
+ return nil, nil
+ }
+ labelCommandValue, hasLabelCommand := onMap["label_command"]
+ if !hasLabelCommand {
+ return nil, nil
+ }
+
+ // Simple string form: label_command: "my-label"
+ if nameStr, ok := labelCommandValue.(string); ok {
+ frontmatterLog.Printf("Extracted label-command name (shorthand): %s", nameStr)
+ return []string{nameStr}, nil
+ }
+
+ // Map form: label_command: {name: "...", names: [...], events: [...]}
+ if lcMap, ok := labelCommandValue.(map[string]any); ok {
+ var names []string
+ var events []string
+
+ if nameVal, hasName := lcMap["name"]; hasName {
+ if nameStr, ok := nameVal.(string); ok {
+ names = []string{nameStr}
+ } else if nameArray, ok := nameVal.([]any); ok {
+ for _, item := range nameArray {
+ if s, ok := item.(string); ok {
+ names = append(names, s)
+ }
+ }
+ }
+ }
+ if namesVal, hasNames := lcMap["names"]; hasNames {
+ if namesArray, ok := namesVal.([]any); ok {
+ for _, item := range namesArray {
+ if s, ok := item.(string); ok {
+ names = append(names, s)
+ }
+ }
+ } else if namesStr, ok := namesVal.(string); ok {
+ names = append(names, namesStr)
+ }
+ }
+
+ if eventsVal, hasEvents := lcMap["events"]; hasEvents {
+ events = ParseCommandEvents(eventsVal)
+ }
+
+ frontmatterLog.Printf("Extracted label-command config: names=%v, events=%v", names, events)
+ return names, events
+ }
+
+ return nil, nil
+}
+
// isGitHubAppNestedField returns true if the trimmed YAML line represents a known
// nested field or array item inside an on.github-app object.
func isGitHubAppNestedField(trimmedLine string) bool {
diff --git a/pkg/workflow/frontmatter_types.go b/pkg/workflow/frontmatter_types.go
index 0395514511b..66518fa8f3c 100644
--- a/pkg/workflow/frontmatter_types.go
+++ b/pkg/workflow/frontmatter_types.go
@@ -87,11 +87,13 @@ type PluginsConfig struct {
}
// APMDependenciesInfo encapsulates APM (Agent Package Manager) dependency configuration.
-// Supports simple array format and object format with packages and isolated fields.
+// Supports simple array format and object format with packages, isolated, github-app, and version fields.
// When present, a pack step is emitted in the activation job and a restore step in the agent job.
type APMDependenciesInfo struct {
- Packages []string // APM package slugs to install (e.g., "org/package")
- Isolated bool // If true, agent restore step clears primitive dirs before unpacking
+ Packages []string // APM package slugs to install (e.g., "org/package")
+ Isolated bool // If true, agent restore step clears primitive dirs before unpacking
+ GitHubApp *GitHubAppConfig // Optional GitHub App for cross-org private package access
+ Version string // Optional APM CLI version override (e.g., "v0.8.0"); defaults to DefaultAPMVersion
}
// RateLimitConfig represents rate limiting configuration for workflow triggers
diff --git a/pkg/workflow/gemini_engine.go b/pkg/workflow/gemini_engine.go
index 8987697fdab..6d1676480fe 100644
--- a/pkg/workflow/gemini_engine.go
+++ b/pkg/workflow/gemini_engine.go
@@ -240,6 +240,10 @@ func (e *GeminiEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str
workflowData.Tools,
workflowData.Runtimes,
)
+ // Add GHES/custom API target domains to the firewall allow-list when engine.api-target is set
+ if workflowData.EngineConfig != nil && workflowData.EngineConfig.APITarget != "" {
+ allowedDomains = mergeAPITargetDomains(allowedDomains, workflowData.EngineConfig.APITarget)
+ }
npmPathSetup := GetNpmBinPathSetup()
geminiCommandWithPath := fmt.Sprintf("%s && %s", npmPathSetup, geminiCommand)
diff --git a/pkg/workflow/gemini_mcp.go b/pkg/workflow/gemini_mcp.go
index 89e0494dc84..699575c4f17 100644
--- a/pkg/workflow/gemini_mcp.go
+++ b/pkg/workflow/gemini_mcp.go
@@ -12,54 +12,15 @@ var geminiMCPLog = logger.New("workflow:gemini_mcp")
func (e *GeminiEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]any, mcpTools []string, workflowData *WorkflowData) error {
geminiMCPLog.Printf("Rendering MCP config for Gemini: tool_count=%d, mcp_tool_count=%d", len(tools), len(mcpTools))
- // Create unified renderer with Gemini-specific options
- createRenderer := func(isLast bool) *MCPConfigRendererUnified {
- return NewMCPConfigRenderer(MCPRendererOptions{
- IncludeCopilotFields: false,
- InlineArgs: false,
- Format: "json", // Gemini uses JSON format like Claude/Codex
- IsLast: isLast,
- ActionMode: GetActionModeFromWorkflowData(workflowData),
- WriteSinkGuardPolicies: deriveWriteSinkGuardPolicyFromWorkflow(workflowData),
- })
- }
+ // Gemini uses JSON format without Copilot-specific fields and multi-line args
+ createRenderer := buildMCPRendererFactory(workflowData, "json", false, false)
// Use shared JSON MCP config renderer
return RenderJSONMCPConfig(yaml, tools, mcpTools, workflowData, JSONMCPConfigOptions{
ConfigPath: "/tmp/gh-aw/mcp-config/mcp-servers.json",
GatewayConfig: buildMCPGatewayConfig(workflowData),
- Renderers: MCPToolRenderers{
- RenderGitHub: func(yaml *strings.Builder, githubTool any, isLast bool, workflowData *WorkflowData) {
- renderer := createRenderer(isLast)
- renderer.RenderGitHubMCP(yaml, githubTool, workflowData)
- },
- RenderPlaywright: func(yaml *strings.Builder, playwrightTool any, isLast bool) {
- renderer := createRenderer(isLast)
- renderer.RenderPlaywrightMCP(yaml, playwrightTool)
- },
- RenderSerena: func(yaml *strings.Builder, serenaTool any, isLast bool) {
- renderer := createRenderer(isLast)
- renderer.RenderSerenaMCP(yaml, serenaTool)
- },
- RenderCacheMemory: noOpCacheMemoryRenderer,
- RenderAgenticWorkflows: func(yaml *strings.Builder, isLast bool) {
- renderer := createRenderer(isLast)
- renderer.RenderAgenticWorkflowsMCP(yaml)
- },
- RenderSafeOutputs: func(yaml *strings.Builder, isLast bool, workflowData *WorkflowData) {
- renderer := createRenderer(isLast)
- renderer.RenderSafeOutputsMCP(yaml, workflowData)
- },
- RenderMCPScripts: func(yaml *strings.Builder, mcpScripts *MCPScriptsConfig, isLast bool) {
- renderer := createRenderer(isLast)
- renderer.RenderMCPScriptsMCP(yaml, mcpScripts, workflowData)
- },
- RenderWebFetch: func(yaml *strings.Builder, isLast bool) {
- renderMCPFetchServerConfig(yaml, "json", " ", isLast, false, deriveWriteSinkGuardPolicyFromWorkflow(workflowData))
- },
- RenderCustomMCPConfig: func(yaml *strings.Builder, toolName string, toolConfig map[string]any, isLast bool) error {
- return renderCustomMCPConfigWrapperWithContext(yaml, toolName, toolConfig, isLast, workflowData)
- },
- },
+ Renderers: buildStandardJSONMCPRenderers(workflowData, createRenderer, false, func(yaml *strings.Builder, toolName string, toolConfig map[string]any, isLast bool) error {
+ return renderCustomMCPConfigWrapperWithContext(yaml, toolName, toolConfig, isLast, workflowData)
+ }),
})
}
diff --git a/pkg/workflow/git_config_test.go b/pkg/workflow/git_config_test.go
index 213b296875b..25d800ffff9 100644
--- a/pkg/workflow/git_config_test.go
+++ b/pkg/workflow/git_config_test.go
@@ -193,15 +193,16 @@ func TestGitCredentialsCleanerStepsHelper(t *testing.T) {
steps := compiler.generateGitCredentialsCleanerStep()
- // Verify we get expected number of lines (2 lines: name and run)
- if len(steps) != 2 {
- t.Errorf("Expected 2 lines in git credentials cleaner steps, got %d", len(steps))
+ // Verify we get expected number of lines (3 lines: name, continue-on-error, and run)
+ if len(steps) != 3 {
+ t.Errorf("Expected 3 lines in git credentials cleaner steps, got %d", len(steps))
}
// Verify the content of the steps
expectedContents := []string{
"Clean git credentials",
- "run: bash /opt/gh-aw/actions/clean_git_credentials.sh",
+ "continue-on-error: true",
+ "run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh",
}
fullContent := strings.Join(steps, "")
@@ -217,3 +218,54 @@ func TestGitCredentialsCleanerStepsHelper(t *testing.T) {
t.Error("Expected first line to have proper indentation for job step (6 spaces)")
}
}
+
+// TestGitConfigurationSkippedWhenCheckoutDisabled verifies that git credential steps
+// are not emitted when checkout: false is set in the workflow frontmatter.
+func TestGitConfigurationSkippedWhenCheckoutDisabled(t *testing.T) {
+ tmpDir := testutil.TempDir(t, "git-config-checkout-false-test")
+
+ testContent := `---
+on: issues
+permissions:
+ issues: read
+engine: copilot
+checkout: false
+---
+
+# Test Workflow (no checkout)
+
+This workflow uses API tools only and does not need the repository to be checked out.
+`
+
+ testFile := filepath.Join(tmpDir, "test-no-checkout.md")
+ if err := os.WriteFile(testFile, []byte(testContent), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ compiler := NewCompiler()
+ compiler.SetSkipValidation(true)
+
+ workflowData, err := compiler.ParseWorkflowFile(testFile)
+ if err != nil {
+ t.Fatalf("Failed to parse workflow file: %v", err)
+ }
+
+ lockContent, err := compiler.generateYAML(workflowData, testFile)
+ if err != nil {
+ t.Fatalf("Failed to generate YAML: %v", err)
+ }
+
+ // When checkout: false, the agent job must NOT contain "Configure Git credentials"
+ // since there is no .git directory and git remote set-url origin would fail.
+ if strings.Contains(lockContent, "Configure Git credentials") {
+ t.Error("'Configure Git credentials' step must NOT be present when checkout: false (no .git directory)")
+ }
+
+ // The "Clean git credentials" step should still be present (resilient, continue-on-error).
+ // Assert that the cleaner step block itself contains both the name and continue-on-error
+ // to avoid false positives from other steps that also use continue-on-error.
+ const cleanerStepBlock = "- name: Clean git credentials\n continue-on-error: true\n run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh"
+ if !strings.Contains(lockContent, cleanerStepBlock) {
+ t.Error("Expected 'Clean git credentials' step with 'continue-on-error: true' to be present when checkout: false")
+ }
+}
diff --git a/pkg/workflow/git_configuration_steps.go b/pkg/workflow/git_configuration_steps.go
index 863492bd8b3..9173389b534 100644
--- a/pkg/workflow/git_configuration_steps.go
+++ b/pkg/workflow/git_configuration_steps.go
@@ -68,10 +68,13 @@ func getGitIdentityEnvVars() map[string]string {
}
// generateGitCredentialsCleanerStep generates a step that removes git credentials from .git/config
-// This is a security measure to prevent credentials left by injected steps from being accessed by the agent
+// This is a security measure to prevent credentials left by injected steps from being accessed by the agent.
+// The step uses continue-on-error to remain resilient when no .git directory exists (e.g. checkout: false)
+// or when git is not installed.
func (c *Compiler) generateGitCredentialsCleanerStep() []string {
return []string{
" - name: Clean git credentials\n",
- " run: bash /opt/gh-aw/actions/clean_git_credentials.sh\n",
+ " continue-on-error: true\n",
+ " run: bash " + GhAwHome + "/actions/clean_git_credentials.sh\n",
}
}
diff --git a/pkg/workflow/github_cli.go b/pkg/workflow/github_cli.go
index 7a80774b83b..ec78318a0e5 100644
--- a/pkg/workflow/github_cli.go
+++ b/pkg/workflow/github_cli.go
@@ -73,6 +73,32 @@ func ExecGHContext(ctx context.Context, args ...string) *exec.Cmd {
return setupGHCommand(ctx, args...)
}
+// runGHWithSpinnerContext executes a gh CLI command with context support, a spinner,
+// and returns the output. This is the core implementation for RunGHContext.
+func runGHWithSpinnerContext(ctx context.Context, spinnerMessage string, combined bool, args ...string) ([]byte, error) {
+ cmd := ExecGHContext(ctx, args...)
+
+ // Show spinner in interactive terminals
+ if tty.IsStderrTerminal() {
+ spinner := console.NewSpinner(spinnerMessage)
+ spinner.Start()
+ var output []byte
+ var err error
+ if combined {
+ output, err = cmd.CombinedOutput()
+ } else {
+ output, err = cmd.Output()
+ }
+ spinner.Stop()
+ return output, err
+ }
+
+ if combined {
+ return cmd.CombinedOutput()
+ }
+ return cmd.Output()
+}
+
// runGHWithSpinner executes a gh CLI command with a spinner and returns the output.
// This is the core implementation shared by RunGH and RunGHCombined.
func runGHWithSpinner(spinnerMessage string, combined bool, args ...string) ([]byte, error) {
@@ -110,6 +136,17 @@ func RunGH(spinnerMessage string, args ...string) ([]byte, error) {
return runGHWithSpinner(spinnerMessage, false, args...)
}
+// RunGHContext executes a gh CLI command with context support (for cancellation/timeout), a
+// spinner, and returns the stdout output. The spinner is shown in interactive terminals to
+// provide feedback during network operations.
+//
+// Usage:
+//
+// output, err := RunGHContext(ctx, "Fetching user info...", "api", "/user")
+func RunGHContext(ctx context.Context, spinnerMessage string, args ...string) ([]byte, error) {
+ return runGHWithSpinnerContext(ctx, spinnerMessage, false, args...)
+}
+
// RunGHCombined executes a gh CLI command with a spinner and returns combined stdout+stderr output.
// The spinner is shown in interactive terminals to provide feedback during network operations.
// Use this when you need to capture error messages from stderr.
diff --git a/pkg/workflow/github_cli_wasm.go b/pkg/workflow/github_cli_wasm.go
index 809e513ae37..0b836037e5e 100644
--- a/pkg/workflow/github_cli_wasm.go
+++ b/pkg/workflow/github_cli_wasm.go
@@ -35,6 +35,10 @@ func RunGH(spinnerMessage string, args ...string) ([]byte, error) {
return nil, fmt.Errorf("gh CLI not available in Wasm")
}
+func RunGHContext(ctx context.Context, spinnerMessage string, args ...string) ([]byte, error) {
+ return nil, fmt.Errorf("gh CLI not available in Wasm")
+}
+
func RunGHCombined(spinnerMessage string, args ...string) ([]byte, error) {
return nil, fmt.Errorf("gh CLI not available in Wasm")
}
diff --git a/pkg/workflow/github_lockdown_autodetect_test.go b/pkg/workflow/github_lockdown_autodetect_test.go
index 7f43f41df56..2901e5cc83b 100644
--- a/pkg/workflow/github_lockdown_autodetect_test.go
+++ b/pkg/workflow/github_lockdown_autodetect_test.go
@@ -15,12 +15,12 @@ func TestGitHubLockdownAutodetection(t *testing.T) {
tests := []struct {
name string
workflow string
- expectedLockdown string // "true" means hardcoded true, "auto" means automatic detection, "none" means no lockdown setting at all
+ expectedGuardPolicy string // "auto" means from step outputs, "static" means hardcoded, "none" means no guard-policy
expectAutoDetectionStep bool // true if automatic detection step should be present
description string
}{
{
- name: "Automatic detection when lockdown not specified",
+ name: "Automatic detection when guard policy not specified",
workflow: `---
on: issues
engine: copilot
@@ -32,11 +32,11 @@ tools:
# Test Workflow
-Test that automatic lockdown detection is enabled when lockdown is not specified.
+Test that automatic guard policy detection is enabled when guard policy is not specified.
`,
- expectedLockdown: "auto",
+ expectedGuardPolicy: "auto",
expectAutoDetectionStep: true,
- description: "When lockdown is not specified, automatic detection step should be present",
+ description: "When guard policy is not specified, automatic detection step should be present with env var refs",
},
{
name: "Lockdown enabled when explicitly set to true",
@@ -54,29 +54,30 @@ tools:
Test with explicit lockdown enabled.
`,
- expectedLockdown: "true",
- expectAutoDetectionStep: false,
- description: "When lockdown is explicitly true, lockdown should be hardcoded",
+ expectedGuardPolicy: "auto",
+ expectAutoDetectionStep: true,
+ description: "When lockdown is explicitly true but no guard policy, auto detection step should still run",
},
{
- name: "No lockdown when explicitly set to false",
+ name: "No auto detection when guard policy explicitly configured",
workflow: `---
on: issues
engine: copilot
tools:
github:
mode: local
- lockdown: false
+ repos: "all"
+ min-integrity: approved
toolsets: [default]
---
# Test Workflow
-Test with explicit lockdown disabled.
+Test with explicit guard policy configured.
`,
- expectedLockdown: "none",
+ expectedGuardPolicy: "static",
expectAutoDetectionStep: false,
- description: "When lockdown is explicitly false, no lockdown setting should be present",
+ description: "When guard policy is explicitly configured, no auto detection step",
},
{
name: "Automatic detection with remote mode when not specified",
@@ -91,11 +92,11 @@ tools:
# Test Workflow
-Test that remote mode uses automatic detection when lockdown not specified.
+Test that remote mode uses automatic detection when guard policy not specified.
`,
- expectedLockdown: "auto",
+ expectedGuardPolicy: "auto",
expectAutoDetectionStep: true,
- description: "Remote mode without explicit lockdown should use automatic detection",
+ description: "Remote mode without explicit guard policy should use automatic detection",
},
}
@@ -139,30 +140,39 @@ Test that remote mode uses automatic detection when lockdown not specified.
t.Errorf("%s: Did not expect automatic detection step but it was found", tt.description)
}
- // Check lockdown configuration based on expected value
- switch tt.expectedLockdown {
- case "true":
- // Should have hardcoded GITHUB_LOCKDOWN_MODE=1 or X-MCP-Lockdown: true
- hasDockerLockdown := strings.Contains(yaml, `"GITHUB_LOCKDOWN_MODE": "1"`)
- hasRemoteLockdown := strings.Contains(yaml, "X-MCP-Lockdown") && strings.Contains(yaml, "\"true\"")
- if !hasDockerLockdown && !hasRemoteLockdown {
- t.Errorf("%s: Expected hardcoded lockdown setting", tt.description)
+ // Check guard policy configuration based on expected value
+ switch tt.expectedGuardPolicy {
+ case "static":
+ // Should have hardcoded guard policy values (not env var refs)
+ hasStaticGuardPolicy := strings.Contains(yaml, `"guard-policies"`) &&
+ !strings.Contains(yaml, `$GITHUB_MCP_GUARD_MIN_INTEGRITY`)
+ if !hasStaticGuardPolicy {
+ t.Errorf("%s: Expected static guard policy but not found", tt.description)
}
case "auto":
- // Should use step output expression for lockdown
- hasStepOutput := strings.Contains(yaml, "steps.determine-automatic-lockdown.outputs.lockdown")
- if !hasStepOutput {
- t.Errorf("%s: Expected lockdown to use step output expression", tt.description)
+ // Should use step output env vars for guard policies
+ hasGuardEnvVars := strings.Contains(yaml, "GITHUB_MCP_GUARD_MIN_INTEGRITY") &&
+ strings.Contains(yaml, "GITHUB_MCP_GUARD_REPOS")
+ if !hasGuardEnvVars {
+ t.Errorf("%s: Expected guard policy env vars from step output", tt.description)
}
- case "none":
- // Should not have GITHUB_LOCKDOWN_MODE or X-MCP-Lockdown (unless using step output)
- if strings.Contains(yaml, `"GITHUB_LOCKDOWN_MODE": "1"`) {
- t.Errorf("%s: Expected no hardcoded lockdown setting", tt.description)
+ // Should reference step outputs in Start MCP Gateway env
+ hasStepOutputRef := strings.Contains(yaml, "steps.determine-automatic-lockdown.outputs.min_integrity") &&
+ strings.Contains(yaml, "steps.determine-automatic-lockdown.outputs.repos")
+ if !hasStepOutputRef {
+ t.Errorf("%s: Expected step output references in Start MCP Gateway env", tt.description)
}
- if strings.Contains(yaml, "X-MCP-Lockdown") && !strings.Contains(yaml, "steps.determine-automatic-lockdown") {
- t.Errorf("%s: Expected no hardcoded lockdown setting", tt.description)
+ case "none":
+ // Should not have guard policy at all
+ if strings.Contains(yaml, `"guard-policies"`) {
+ t.Errorf("%s: Expected no guard policy but found one", tt.description)
}
}
+
+ // Verify lockdown is no longer automatically set from step output
+ if strings.Contains(yaml, "steps.determine-automatic-lockdown.outputs.lockdown") {
+ t.Errorf("%s: lockdown output should no longer be automatically emitted from step", tt.description)
+ }
})
}
}
@@ -209,16 +219,24 @@ Test that Claude engine has no automatic lockdown determination.
}
yaml := string(lockContent)
- // Verify automatic detection step is present (lockdown not explicitly set)
+ // Verify automatic detection step is present (guard policy not explicitly set)
detectStepPresent := strings.Contains(yaml, "Determine automatic lockdown mode for GitHub MCP Server") &&
strings.Contains(yaml, "determine-automatic-lockdown")
if !detectStepPresent {
- t.Error("Determination step should be present for Claude engine when lockdown not explicitly set")
+ t.Error("Determination step should be present for Claude engine when guard policy not explicitly set")
+ }
+
+ // Check that guard policy env vars are referenced (not lockdown)
+ if !strings.Contains(yaml, "GITHUB_MCP_GUARD_MIN_INTEGRITY") {
+ t.Error("Expected GITHUB_MCP_GUARD_MIN_INTEGRITY env var for Claude engine")
+ }
+ if !strings.Contains(yaml, "GITHUB_MCP_GUARD_REPOS") {
+ t.Error("Expected GITHUB_MCP_GUARD_REPOS env var for Claude engine")
}
- // Check if lockdown uses step output expression
- if !strings.Contains(yaml, "steps.determine-automatic-lockdown.outputs.lockdown") {
- t.Error("Expected lockdown to use step output expression for Claude engine")
+ // Verify lockdown is no longer automatically set from step output
+ if strings.Contains(yaml, "steps.determine-automatic-lockdown.outputs.lockdown") {
+ t.Error("lockdown output should no longer be automatically emitted from step")
}
}
diff --git a/pkg/workflow/importable_tools_test.go b/pkg/workflow/importable_tools_test.go
index d1bc8396b65..60a58047035 100644
--- a/pkg/workflow/importable_tools_test.go
+++ b/pkg/workflow/importable_tools_test.go
@@ -240,7 +240,7 @@ Uses imported agentic-workflows tool.
}
// Verify binary mounts are NOT present in dev mode
- if strings.Contains(workflowData, `/opt/gh-aw:/opt/gh-aw:ro`) {
+ if strings.Contains(workflowData, `${GH_AW_HOME}:${GH_AW_HOME}:ro`) {
t.Error("Did not expect /opt/gh-aw mount in dev mode (binary is in image)")
}
diff --git a/pkg/workflow/inference_access_error_test.go b/pkg/workflow/inference_access_error_test.go
index 64385ab64fa..c67b1157383 100644
--- a/pkg/workflow/inference_access_error_test.go
+++ b/pkg/workflow/inference_access_error_test.go
@@ -49,7 +49,7 @@ Test workflow`
}
// Check that the detection step calls the shell script
- if !strings.Contains(lockStr, "bash /opt/gh-aw/actions/detect_inference_access_error.sh") {
+ if !strings.Contains(lockStr, "bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh") {
t.Error("Expected detect-inference-error step to call detect_inference_access_error.sh")
}
diff --git a/pkg/workflow/jobs.go b/pkg/workflow/jobs.go
index 3f9cd7c6111..7aa397d2e9e 100644
--- a/pkg/workflow/jobs.go
+++ b/pkg/workflow/jobs.go
@@ -175,6 +175,7 @@ func (jm *JobManager) RenderToYAML() string {
// renderJob renders a single job to YAML
func (jm *JobManager) renderJob(job *Job) string {
+ jobLog.Printf("Rendering job: %s (steps=%d, needs=%d, reusable=%t)", job.Name, len(job.Steps), len(job.Needs), job.Uses != "")
var yaml strings.Builder
fmt.Fprintf(&yaml, " %s:\n", job.Name)
@@ -310,6 +311,7 @@ func (jm *JobManager) renderJob(job *Job) string {
// Check if this is a reusable workflow call
if job.Uses != "" {
+ jobLog.Printf("Rendering reusable workflow call: %s uses=%s with=%d secrets=%d", job.Name, job.Uses, len(job.With), len(job.Secrets))
// Add uses directive for reusable workflow
fmt.Fprintf(&yaml, " uses: %s\n", job.Uses)
diff --git a/pkg/workflow/label_command.go b/pkg/workflow/label_command.go
new file mode 100644
index 00000000000..f46c3393b26
--- /dev/null
+++ b/pkg/workflow/label_command.go
@@ -0,0 +1,95 @@
+package workflow
+
+import (
+ "errors"
+ "slices"
+
+ "github.com/github/gh-aw/pkg/logger"
+)
+
+var labelCommandLog = logger.New("workflow:label_command")
+
+// labelCommandSupportedEvents defines the GitHub Actions events that support label-command triggers
+var labelCommandSupportedEvents = []string{"issues", "pull_request", "discussion"}
+
+// FilterLabelCommandEvents returns the label-command events to use based on the specified identifiers.
+// If identifiers is nil or empty, returns all supported events.
+func FilterLabelCommandEvents(identifiers []string) []string {
+ if len(identifiers) == 0 {
+ labelCommandLog.Print("No label-command event identifiers specified, returning all events")
+ return labelCommandSupportedEvents
+ }
+
+ var result []string
+ for _, id := range identifiers {
+ if slices.Contains(labelCommandSupportedEvents, id) {
+ result = append(result, id)
+ }
+ }
+
+ labelCommandLog.Printf("Filtered label-command events: %v -> %v", identifiers, result)
+ return result
+}
+
+// buildLabelCommandCondition creates a condition that checks whether the triggering label
+// matches one of the configured label-command names. For non-label events (e.g.
+// workflow_dispatch and any other events in LabelCommandOtherEvents), the condition
+// passes unconditionally so that manual runs and other triggers still work.
+func buildLabelCommandCondition(labelNames []string, labelCommandEvents []string, hasOtherEvents bool) (ConditionNode, error) {
+ labelCommandLog.Printf("Building label-command condition: labels=%v, events=%v, has_other_events=%t",
+ labelNames, labelCommandEvents, hasOtherEvents)
+
+ if len(labelNames) == 0 {
+ return nil, errors.New("no label names provided for label-command trigger")
+ }
+
+ filteredEvents := FilterLabelCommandEvents(labelCommandEvents)
+ if len(filteredEvents) == 0 {
+ return nil, errors.New("no valid events specified for label-command trigger")
+ }
+
+ // Build the label-name match condition: label1 == name OR label2 == name ...
+ var labelNameChecks []ConditionNode
+ for _, labelName := range labelNames {
+ labelNameChecks = append(labelNameChecks, BuildEquals(
+ BuildPropertyAccess("github.event.label.name"),
+ BuildStringLiteral(labelName),
+ ))
+ }
+ var labelNameMatch ConditionNode
+ if len(labelNameChecks) == 1 {
+ labelNameMatch = labelNameChecks[0]
+ } else {
+ labelNameMatch = BuildDisjunction(false, labelNameChecks...)
+ }
+
+ // Build per-event checks: (event_name == 'issues' AND label matches) OR ...
+ var eventChecks []ConditionNode
+ for _, event := range filteredEvents {
+ eventChecks = append(eventChecks, &AndNode{
+ Left: BuildEventTypeEquals(event),
+ Right: labelNameMatch,
+ })
+ }
+ labelCondition := BuildDisjunction(false, eventChecks...)
+
+ if !hasOtherEvents {
+ // No other events — the label condition is the entire condition.
+ return labelCondition, nil
+ }
+
+ // When there are other events (e.g. workflow_dispatch from the expanded shorthand, or
+ // user-supplied events), we allow non-label events through unconditionally and only
+ // require the label-name check for label events.
+ var labelEventChecks []ConditionNode
+ for _, event := range filteredEvents {
+ labelEventChecks = append(labelEventChecks, BuildEventTypeEquals(event))
+ }
+ isLabelEvent := BuildDisjunction(false, labelEventChecks...)
+ isNotLabelEvent := &NotNode{Child: isLabelEvent}
+
+ return &OrNode{
+ Left: &AndNode{Left: isLabelEvent, Right: labelNameMatch},
+ Right: isNotLabelEvent,
+ }, nil
+}
diff --git a/pkg/workflow/label_command_parser.go b/pkg/workflow/label_command_parser.go
new file mode 100644
index 00000000000..0115a0b89b4
--- /dev/null
+++ b/pkg/workflow/label_command_parser.go
@@ -0,0 +1,19 @@
+package workflow
+
+import (
+ "github.com/github/gh-aw/pkg/logger"
+)
+
+var labelCommandParserLog = logger.New("workflow:label_command_parser")
+
+// expandLabelCommandShorthand takes a label name and returns a map that represents
+// the expanded label_command + workflow_dispatch configuration.
+// This is the intermediate form stored in the frontmatter "on" map before
+// parseOnSection processes it into WorkflowData.LabelCommand.
+func expandLabelCommandShorthand(labelName string) map[string]any {
+ labelCommandParserLog.Printf("Expanding label-command shorthand for label: %s", labelName)
+ return map[string]any{
+ "label_command": labelName,
+ "workflow_dispatch": nil,
+ }
+}
diff --git a/pkg/workflow/label_command_test.go b/pkg/workflow/label_command_test.go
new file mode 100644
index 00000000000..28beac8f530
--- /dev/null
+++ b/pkg/workflow/label_command_test.go
@@ -0,0 +1,455 @@
+//go:build !integration
+
+package workflow
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/github/gh-aw/pkg/stringutil"
+
+ "github.com/goccy/go-yaml"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+// TestLabelCommandShorthandPreprocessing verifies that "label-command " shorthand
+// is expanded into the label_command map form by the schedule preprocessor.
+func TestLabelCommandShorthandPreprocessing(t *testing.T) {
+ tests := []struct {
+ name string
+ onValue string
+ wantLabelName string
+ wantErr bool
+ }{
+ {
+ name: "simple label-command shorthand",
+ onValue: "label-command deploy",
+ wantLabelName: "deploy",
+ },
+ {
+ name: "label-command with hyphenated label",
+ onValue: "label-command needs-review",
+ wantLabelName: "needs-review",
+ },
+ {
+ name: "label-command without label name",
+ onValue: "label-command ",
+ wantErr: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ frontmatter := map[string]any{
+ "on": tt.onValue,
+ }
+
+ compiler := NewCompiler()
+ err := compiler.preprocessScheduleFields(frontmatter, "", "")
+ if tt.wantErr {
+ assert.Error(t, err, "expected error for input %q", tt.onValue)
+ return
+ }
+
+ require.NoError(t, err, "preprocessScheduleFields() should not error")
+
+ onVal := frontmatter["on"]
+ onMap, ok := onVal.(map[string]any)
+ require.True(t, ok, "on field should be a map after expansion, got %T", onVal)
+
+ labelCmd, hasLabel := onMap["label_command"]
+ require.True(t, hasLabel, "on map should have label_command key")
+ assert.Equal(t, tt.wantLabelName, labelCmd,
+ "label_command value should be %q", tt.wantLabelName)
+
+ _, hasDispatch := onMap["workflow_dispatch"]
+ assert.True(t, hasDispatch, "on map should have workflow_dispatch key")
+ })
+ }
+}
+
+// TestExpandLabelCommandShorthand verifies the expand helper function.
+func TestExpandLabelCommandShorthand(t *testing.T) {
+ result := expandLabelCommandShorthand("deploy")
+
+ labelCmd, ok := result["label_command"]
+ require.True(t, ok, "expanded map should have label_command key")
+ assert.Equal(t, "deploy", labelCmd, "label_command should equal the label name")
+
+ _, hasDispatch := result["workflow_dispatch"]
+ assert.True(t, hasDispatch, "expanded map should have workflow_dispatch key")
+}
+
+// TestFilterLabelCommandEvents verifies that FilterLabelCommandEvents returns correct subsets.
+func TestFilterLabelCommandEvents(t *testing.T) {
+ tests := []struct {
+ name string
+ identifiers []string
+ want []string
+ }{
+ {
+ name: "nil identifiers returns all events",
+ identifiers: nil,
+ want: []string{"issues", "pull_request", "discussion"},
+ },
+ {
+ name: "empty identifiers returns all events",
+ identifiers: []string{},
+ want: []string{"issues", "pull_request", "discussion"},
+ },
+ {
+ name: "single issues event",
+ identifiers: []string{"issues"},
+ want: []string{"issues"},
+ },
+ {
+ name: "issues and pull_request only",
+ identifiers: []string{"issues", "pull_request"},
+ want: []string{"issues", "pull_request"},
+ },
+ {
+ name: "unsupported event is filtered out",
+ identifiers: []string{"issues", "unknown_event"},
+ want: []string{"issues"},
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := FilterLabelCommandEvents(tt.identifiers)
+ assert.Equal(t, tt.want, got, "FilterLabelCommandEvents(%v)", tt.identifiers)
+ })
+ }
+}
+
+// TestBuildLabelCommandCondition verifies the condition builder for label-command triggers.
+func TestBuildLabelCommandCondition(t *testing.T) {
+ tests := []struct {
+ name string
+ labelNames []string
+ events []string
+ hasOtherEvents bool
+ wantErr bool
+ wantContains []string
+ wantNotContains []string
+ }{
+ {
+ name: "single label all events no other events",
+ labelNames: []string{"deploy"},
+ events: nil,
+ wantContains: []string{
+ "github.event.label.name == 'deploy'",
+ "github.event_name == 'issues'",
+ "github.event_name == 'pull_request'",
+ "github.event_name == 'discussion'",
+ },
+ },
+ {
+ name: "multiple labels all events",
+ labelNames: []string{"deploy", "release"},
+ events: nil,
+ wantContains: []string{
+ "github.event.label.name == 'deploy'",
+ "github.event.label.name == 'release'",
+ },
+ },
+ {
+ name: "single label issues only",
+ labelNames: []string{"triage"},
+ events: []string{"issues"},
+ wantContains: []string{
+ "github.event_name == 'issues'",
+ "github.event.label.name == 'triage'",
+ },
+ wantNotContains: []string{
+ "github.event_name == 'pull_request'",
+ "github.event_name == 'discussion'",
+ },
+ },
+ {
+ name: "no label names returns error",
+ labelNames: []string{},
+ wantErr: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ condition, err := buildLabelCommandCondition(tt.labelNames, tt.events, tt.hasOtherEvents)
+ if tt.wantErr {
+ assert.Error(t, err, "expected an error")
+ return
+ }
+
+ require.NoError(t, err, "buildLabelCommandCondition() should not error")
+ rendered := condition.Render()
+
+ for _, want := range tt.wantContains {
+ assert.Contains(t, rendered, want,
+ "condition should contain %q, got: %s", want, rendered)
+ }
+ for _, notWant := range tt.wantNotContains {
+ assert.NotContains(t, rendered, notWant,
+ "condition should NOT contain %q, got: %s", notWant, rendered)
+ }
+ })
+ }
+}
+
+// TestLabelCommandWorkflowCompile verifies that a workflow with label_command trigger
+// compiles to a valid GitHub Actions workflow with:
+// - label-based events (issues, pull_request, discussion) in the on: section
+// - workflow_dispatch with item_number input
+// - a label-name condition in the activation job's if:
+// - a remove_trigger_label step in the activation job
+// - a label_command output on the activation job
+func TestLabelCommandWorkflowCompile(t *testing.T) {
+ tempDir := t.TempDir()
+
+ workflowContent := `---
+name: Label Command Test
+on:
+ label_command: deploy
+engine: copilot
+---
+
+Deploy the application because label "deploy" was added.
+`
+
+ workflowPath := filepath.Join(tempDir, "label-command-test.md")
+ err := os.WriteFile(workflowPath, []byte(workflowContent), 0644)
+ require.NoError(t, err, "failed to write test workflow")
+
+ compiler := NewCompiler()
+
+ // Parse the workflow first to verify defaults are set during parsing
+ workflowData, err := compiler.ParseWorkflowFile(workflowPath)
+ require.NoError(t, err, "ParseWorkflowFile() should not error")
+
+ // Verify AIReaction defaults to "eyes" for label_command workflows
+ assert.Equal(t, "eyes", workflowData.AIReaction, "AIReaction should default to 'eyes' for label_command workflows")
+
+ // Verify StatusComment defaults to true for label_command workflows
+ require.NotNil(t, workflowData.StatusComment, "StatusComment should not be nil for label_command workflows")
+ assert.True(t, *workflowData.StatusComment, "StatusComment should default to true for label_command workflows")
+
+ err = compiler.CompileWorkflow(workflowPath)
+ require.NoError(t, err, "CompileWorkflow() should not error")
+
+ lockFilePath := stringutil.MarkdownToLockFile(workflowPath)
+ lockContent, err := os.ReadFile(lockFilePath)
+ require.NoError(t, err, "failed to read lock file")
+
+ lockStr := string(lockContent)
+
+ // Verify the on: section includes label-based events
+ assert.Contains(t, lockStr, "issues:", "on section should contain issues event")
+ assert.Contains(t, lockStr, "pull_request:", "on section should contain pull_request event")
+ assert.Contains(t, lockStr, "discussion:", "on section should contain discussion event")
+ assert.Contains(t, lockStr, "labeled", "on section should contain labeled type")
+ assert.Contains(t, lockStr, "workflow_dispatch:", "on section should contain workflow_dispatch")
+ assert.Contains(t, lockStr, "item_number:", "workflow_dispatch should include item_number input")
+
+ // Parse the YAML to check the activation job
+ var workflow map[string]any
+ err = yaml.Unmarshal(lockContent, &workflow)
+ require.NoError(t, err, "failed to parse lock file as YAML")
+
+ jobs, ok := workflow["jobs"].(map[string]any)
+ require.True(t, ok, "workflow should have jobs")
+
+ activation, ok := jobs["activation"].(map[string]any)
+ require.True(t, ok, "workflow should have an activation job")
+
+ // Verify the activation job has a label_command output
+ activationOutputs, ok := activation["outputs"].(map[string]any)
+ require.True(t, ok, "activation job should have outputs")
+
+ labelCmdOutput, hasOutput := activationOutputs["label_command"]
+ assert.True(t, hasOutput, "activation job should have label_command output")
+ assert.Contains(t, labelCmdOutput, "remove_trigger_label",
+ "label_command output should reference the remove_trigger_label step")
+
+ // Verify the remove_trigger_label step exists in the activation job
+ activationSteps, ok := activation["steps"].([]any)
+ require.True(t, ok, "activation job should have steps")
+
+ foundRemoveStep := false
+ for _, step := range activationSteps {
+ stepMap, ok := step.(map[string]any)
+ if !ok {
+ continue
+ }
+ if id, ok := stepMap["id"].(string); ok && id == "remove_trigger_label" {
+ foundRemoveStep = true
+ break
+ }
+ }
+ assert.True(t, foundRemoveStep, "activation job should contain a remove_trigger_label step")
+
+ // Verify the compiled workflow includes the default eyes reaction step
+ assert.Contains(t, lockStr, "Add eyes reaction for immediate feedback",
+ "activation job should have eyes reaction step when reaction defaults to 'eyes'")
+
+ // Verify the workflow condition includes the label name check
+ agentJob, hasAgent := jobs["agent"].(map[string]any)
+ require.True(t, hasAgent, "workflow should have an agent job")
+ _ = agentJob // presence check is sufficient
+}
+
+// TestLabelCommandWorkflowCompileShorthand verifies the "label-command " string shorthand.
+func TestLabelCommandWorkflowCompileShorthand(t *testing.T) {
+ tempDir := t.TempDir()
+
+ workflowContent := `---
+name: Label Command Shorthand Test
+on: "label-command needs-review"
+engine: copilot
+---
+
+Triggered by the needs-review label.
+`
+
+ workflowPath := filepath.Join(tempDir, "label-command-shorthand.md")
+ err := os.WriteFile(workflowPath, []byte(workflowContent), 0644)
+ require.NoError(t, err, "failed to write test workflow")
+
+ compiler := NewCompiler()
+ err = compiler.CompileWorkflow(workflowPath)
+ require.NoError(t, err, "CompileWorkflow() should not error for shorthand form")
+
+ lockFilePath := stringutil.MarkdownToLockFile(workflowPath)
+ lockContent, err := os.ReadFile(lockFilePath)
+ require.NoError(t, err, "failed to read lock file")
+
+ lockStr := string(lockContent)
+ assert.Contains(t, lockStr, "labeled", "compiled workflow should contain labeled type")
+ assert.Contains(t, lockStr, "remove_trigger_label", "compiled workflow should contain remove_trigger_label step")
+}
+
+// TestLabelCommandWorkflowWithEvents verifies that specifying events: restricts
+// which GitHub Actions events are generated.
+func TestLabelCommandWorkflowWithEvents(t *testing.T) {
+ tempDir := t.TempDir()
+
+ workflowContent := `---
+name: Label Command Issues Only
+on:
+ label_command:
+ name: deploy
+ events: [issues]
+engine: copilot
+---
+
+Triggered by the deploy label on issues only.
+`
+
+ workflowPath := filepath.Join(tempDir, "label-command-issues-only.md")
+ err := os.WriteFile(workflowPath, []byte(workflowContent), 0644)
+ require.NoError(t, err, "failed to write test workflow")
+
+ compiler := NewCompiler()
+ err = compiler.CompileWorkflow(workflowPath)
+ require.NoError(t, err, "CompileWorkflow() should not error")
+
+ lockFilePath := stringutil.MarkdownToLockFile(workflowPath)
+ lockContent, err := os.ReadFile(lockFilePath)
+ require.NoError(t, err, "failed to read lock file")
+
+ lockStr := string(lockContent)
+
+ // Should have issues event
+ assert.Contains(t, lockStr, "issues:", "on section should contain issues event")
+
+ // workflow_dispatch is always added
+ assert.Contains(t, lockStr, "workflow_dispatch:", "on section should contain workflow_dispatch")
+
+ // pull_request and discussion should NOT be present since events: [issues] was specified
+ // (However, they may be commented or absent — check the YAML structure)
+ var workflow map[string]any
+ err = yaml.Unmarshal(lockContent, &workflow)
+ require.NoError(t, err, "failed to parse lock file as YAML")
+
+ onSection, ok := workflow["on"].(map[string]any)
+ require.True(t, ok, "workflow on: section should be a map")
+
+ _, hasPR := onSection["pull_request"]
+ assert.False(t, hasPR, "pull_request event should not be present when events=[issues]")
+
+ _, hasDiscussion := onSection["discussion"]
+ assert.False(t, hasDiscussion, "discussion event should not be present when events=[issues]")
+}
+
+// TestLabelCommandNoClashWithExistingLabelTrigger verifies that label_command can coexist
+// with an existing label-only issues trigger without creating a duplicate issues: YAML block.
+// The existing issues block types are merged into the label_command-generated issues block.
+func TestLabelCommandNoClashWithExistingLabelTrigger(t *testing.T) {
+ tempDir := t.TempDir()
+
+ // Workflow that has both an explicit "issues: types: [labeled]" block AND label_command.
+ // This is the exact key-clash scenario: without merging, two "issues:" keys would appear
+ // in the compiled YAML, which is invalid and silently broken.
+ workflowContent := `---
+name: No Clash Test
+on:
+ label_command: deploy
+ issues:
+ types: [labeled]
+engine: copilot
+---
+
+Both label-command and existing issues labeled trigger.
+`
+
+ workflowPath := filepath.Join(tempDir, "no-clash-test.md")
+ err := os.WriteFile(workflowPath, []byte(workflowContent), 0644)
+ require.NoError(t, err, "failed to write test workflow")
+
+ compiler := NewCompiler()
+ err = compiler.CompileWorkflow(workflowPath)
+ require.NoError(t, err, "CompileWorkflow() should not error when mixing label_command with existing label trigger")
+
+ lockFilePath := stringutil.MarkdownToLockFile(workflowPath)
+ lockContent, err := os.ReadFile(lockFilePath)
+ require.NoError(t, err, "failed to read lock file")
+
+ lockStr := string(lockContent)
+
+ // Verify there is exactly ONE "issues:" block at the YAML top level
+ // (count occurrences that are a key, not embedded in other values)
+ issuesCount := strings.Count(lockStr, "\n issues:\n") + strings.Count(lockStr, "\nissues:\n")
+ assert.Equal(t, 1, issuesCount,
+ "there should be exactly one 'issues:' trigger block in the compiled YAML, got %d. Compiled:\n%s",
+ issuesCount, lockStr)
+}
+
+// TestLabelCommandConflictWithNonLabelTrigger verifies that using label_command alongside
+// an issues/pull_request trigger with non-label types returns a validation error.
+func TestLabelCommandConflictWithNonLabelTrigger(t *testing.T) {
+ tempDir := t.TempDir()
+
+ // Workflow with label_command and issues: types: [opened] — non-label type conflicts
+ workflowContent := `---
+name: Conflict Test
+on:
+ label_command: deploy
+ issues:
+ types: [opened]
+engine: copilot
+---
+
+This should fail validation.
+`
+
+ workflowPath := filepath.Join(tempDir, "conflict-test.md")
+ err := os.WriteFile(workflowPath, []byte(workflowContent), 0644)
+ require.NoError(t, err, "failed to write test workflow")
+
+ compiler := NewCompiler()
+ err = compiler.CompileWorkflow(workflowPath)
+ require.Error(t, err, "CompileWorkflow() should error when label_command is combined with non-label issues trigger")
+ assert.Contains(t, err.Error(), "label_command", "error should mention label_command")
+}
diff --git a/pkg/workflow/main_job_env_test.go b/pkg/workflow/main_job_env_test.go
index db8144ac82e..b506672cccc 100644
--- a/pkg/workflow/main_job_env_test.go
+++ b/pkg/workflow/main_job_env_test.go
@@ -21,12 +21,15 @@ func TestMainJobEnvironmentVariables(t *testing.T) {
shouldHaveEnv bool
}{
{
- name: "No safe outputs - no env section",
+ name: "No safe outputs - only GH_AW_HOME set",
frontmatter: map[string]any{
"name": "Test Workflow",
"on": "push",
},
- shouldHaveEnv: false,
+ expectedEnvVars: []string{
+ "GH_AW_HOME: " + GhAwHomeExprDefault,
+ },
+ shouldHaveEnv: true,
},
{
name: "Safe outputs with create-issue",
diff --git a/pkg/workflow/maintenance_workflow.go b/pkg/workflow/maintenance_workflow.go
index 51515289cf1..4017cc88a0c 100644
--- a/pkg/workflow/maintenance_workflow.go
+++ b/pkg/workflow/maintenance_workflow.go
@@ -235,7 +235,7 @@ jobs:
yaml.WriteString(` - name: Setup Scripts
uses: ` + setupActionRef + `
with:
- destination: /opt/gh-aw/actions
+ destination: ` + SetupActionDestination + `
- name: Close expired discussions
uses: ` + GetActionPin("actions/github-script") + `
@@ -244,10 +244,10 @@ jobs:
`)
// Add the close expired discussions script using require()
- yaml.WriteString(` const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/close_expired_discussions.cjs');
- await main();
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
+ yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/close_expired_discussions.cjs") + ");\n")
+ yaml.WriteString(` await main();
- name: Close expired issues
uses: ` + GetActionPin("actions/github-script") + `
@@ -256,10 +256,10 @@ jobs:
`)
// Add the close expired issues script using require()
- yaml.WriteString(` const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/close_expired_issues.cjs');
- await main();
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
+ yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/close_expired_issues.cjs") + ");\n")
+ yaml.WriteString(` await main();
- name: Close expired pull requests
uses: ` + GetActionPin("actions/github-script") + `
@@ -268,11 +268,10 @@ jobs:
`)
// Add the close expired pull requests script using require()
- yaml.WriteString(` const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/close_expired_pull_requests.cjs');
- await main();
-`)
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
+ yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/close_expired_pull_requests.cjs") + ");\n")
+ yaml.WriteString(" await main();\n")
// Add unified run_operation job for all dispatch operations
yaml.WriteString(`
@@ -292,19 +291,18 @@ jobs:
- name: Setup Scripts
uses: ` + setupActionRef + `
with:
- destination: /opt/gh-aw/actions
+ destination: ` + SetupActionDestination + `
- name: Check admin/maintainer permissions
uses: ` + GetActionPin("actions/github-script") + `
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_team_member.cjs');
- await main();
-
`)
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
+ yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/check_team_member.cjs") + ");\n")
+ yaml.WriteString(" await main();\n\n")
yaml.WriteString(generateInstallCLISteps(actionMode, version, actionTag))
yaml.WriteString(` - name: Run operation
@@ -316,11 +314,11 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/run_operation_update_upgrade.cjs');
- await main();
`)
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
+ yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/run_operation_update_upgrade.cjs") + ");\n")
+ yaml.WriteString(" await main();\n")
// Add compile-workflows and zizmor-scan jobs only in dev mode
// These jobs are specific to the gh-aw repository and require go.mod, make build, etc.
@@ -354,16 +352,17 @@ jobs:
- name: Setup Scripts
uses: ` + setupActionRef + `
with:
- destination: /opt/gh-aw/actions
+ destination: ` + SetupActionDestination + `
- name: Check for out-of-sync workflows and create issue if needed
uses: ` + GetActionPin("actions/github-script") + `
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_recompile_needed.cjs');
- await main();
+`)
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
+ yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/check_workflow_recompile_needed.cjs") + ");\n")
+ yaml.WriteString(` await main();
zizmor-scan:
if: ${{ !github.event.repository.fork && (github.event_name != 'workflow_dispatch' || github.event.inputs.operation == '') }}
@@ -417,7 +416,7 @@ jobs:
- name: Setup Scripts
uses: ` + setupActionRef + `
with:
- destination: /opt/gh-aw/actions
+ destination: ` + SetupActionDestination + `
- name: Validate Secrets
uses: ` + GetActionPin("actions/github-script") + `
@@ -435,10 +434,11 @@ jobs:
NOTION_API_TOKEN: ${{ secrets.NOTION_API_TOKEN }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/validate_secrets.cjs');
- await main();
+`)
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
+ yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/validate_secrets.cjs") + ");\n")
+ yaml.WriteString(` await main();
- name: Upload secret validation report
if: always()
diff --git a/pkg/workflow/map_helpers.go b/pkg/workflow/map_helpers.go
index a603058cfd2..4474457d333 100644
--- a/pkg/workflow/map_helpers.go
+++ b/pkg/workflow/map_helpers.go
@@ -18,6 +18,7 @@
//
// Type Conversion:
// - parseIntValue() - Safely parse numeric types to int with truncation warnings
+// - safeUint64ToInt() - Convert uint64 to int, returning 0 on overflow
//
// Map Operations:
// - filterMapKeys() - Create new map excluding specified keys
@@ -28,6 +29,8 @@
package workflow
import (
+ "math"
+
"github.com/github/gh-aw/pkg/logger"
)
@@ -61,6 +64,14 @@ func parseIntValue(value any) (int, bool) {
}
}
+// safeUint64ToInt safely converts uint64 to int, returning 0 if overflow would occur
+func safeUint64ToInt(u uint64) int {
+ if u > math.MaxInt {
+ return 0 // Return 0 (engine default) if value would overflow
+ }
+ return int(u)
+}
+
// filterMapKeys creates a new map excluding the specified keys
func filterMapKeys(original map[string]any, excludeKeys ...string) map[string]any {
excludeSet := make(map[string]bool)
diff --git a/pkg/workflow/map_helpers_test.go b/pkg/workflow/map_helpers_test.go
index ea28270ee56..902dde4d4e6 100644
--- a/pkg/workflow/map_helpers_test.go
+++ b/pkg/workflow/map_helpers_test.go
@@ -246,3 +246,51 @@ func TestFilterMapKeys(t *testing.T) {
})
}
}
+
+func TestSafeUint64ToInt(t *testing.T) {
+ tests := []struct {
+ name string
+ value uint64
+ expected int
+ }{
+ {
+ name: "zero",
+ value: 0,
+ expected: 0,
+ },
+ {
+ name: "small value",
+ value: 42,
+ expected: 42,
+ },
+ {
+ name: "large value within int range",
+ value: 1000000,
+ expected: 1000000,
+ },
+ {
+ name: "max int value",
+ value: uint64(^uint(0) >> 1),
+ expected: int(^uint(0) >> 1),
+ },
+ {
+ name: "overflow: max uint64 returns 0",
+ value: ^uint64(0),
+ expected: 0,
+ },
+ {
+ name: "overflow: max int + 1 returns 0",
+ value: uint64(^uint(0)>>1) + 1,
+ expected: 0,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := safeUint64ToInt(tt.value)
+ if result != tt.expected {
+ t.Errorf("safeUint64ToInt(%d) = %d, want %d", tt.value, result, tt.expected)
+ }
+ })
+ }
+}
diff --git a/pkg/workflow/mcp_config_builtin.go b/pkg/workflow/mcp_config_builtin.go
index 0bf99def32e..f5a7e2c7bb1 100644
--- a/pkg/workflow/mcp_config_builtin.go
+++ b/pkg/workflow/mcp_config_builtin.go
@@ -144,18 +144,13 @@ func renderSafeOutputsMCPConfigWithOptions(yaml *strings.Builder, isLast bool, i
}
yaml.WriteString(" }")
- // Check if GitHub tool has guard-policies configured
+ // Check if GitHub tool has guard-policies configured (or auto-lockdown will run)
// If so, generate a linked write-sink guard-policy for safeoutputs
- var guardPolicies map[string]any
- if workflowData != nil && workflowData.Tools != nil {
- if githubTool, hasGitHub := workflowData.Tools["github"]; hasGitHub {
- guardPolicies = deriveSafeOutputsGuardPolicyFromGitHub(githubTool)
- }
- }
+ guardPolicies := deriveWriteSinkGuardPolicyFromWorkflow(workflowData)
// Add guard-policies if configured
if len(guardPolicies) > 0 {
- mcpBuiltinLog.Print("Adding guard-policies to safeoutputs (derived from GitHub guard-policy)")
+ mcpBuiltinLog.Print("Adding guard-policies to safeoutputs (derived from GitHub guard-policy or auto-lockdown detection)")
yaml.WriteString(",\n")
renderGuardPoliciesJSON(yaml, guardPolicies, " ")
} else {
@@ -216,7 +211,7 @@ func renderAgenticWorkflowsMCPConfigWithOptions(yaml *strings.Builder, isLast bo
// Release mode: Use minimal Alpine image with mounted binaries
// The gh-aw binary is mounted from /opt/gh-aw and executed directly
// Pass --validate-actor flag to enable role-based access control
- entrypoint = "/opt/gh-aw/gh-aw"
+ entrypoint = GhAwHome + "/gh-aw"
entrypointArgs = []string{"mcp-server", "--validate-actor"}
// Mount gh-aw binary, gh CLI binary, workspace, and temp directory
mounts = []string{constants.DefaultGhAwMount, constants.DefaultGhBinaryMount, constants.DefaultWorkspaceMount, constants.DefaultTmpGhAwMount}
@@ -228,11 +223,6 @@ func renderAgenticWorkflowsMCPConfigWithOptions(yaml *strings.Builder, isLast bo
// In dev mode, use the container's default ENTRYPOINT
if entrypoint != "" {
yaml.WriteString(" \"entrypoint\": \"" + entrypoint + "\",\n")
- }
-
- // Only write entrypointArgs if specified (release mode)
- // In dev mode, use the container's default CMD
- if entrypointArgs != nil {
yaml.WriteString(" \"entrypointArgs\": [")
for i, arg := range entrypointArgs {
if i > 0 {
diff --git a/pkg/workflow/mcp_config_compilation_test.go b/pkg/workflow/mcp_config_compilation_test.go
index abc6cd6c59e..84c45c22a89 100644
--- a/pkg/workflow/mcp_config_compilation_test.go
+++ b/pkg/workflow/mcp_config_compilation_test.go
@@ -4,6 +4,7 @@ package workflow
import (
"os"
+ "regexp"
"strings"
"testing"
)
@@ -217,9 +218,11 @@ mcp-servers:
Test workflow.
`,
- serverName: `"my-api"`,
- expectedContent: []string{`"get_data"`, `"list_items"`},
- unexpectedInServer: []string{`"*"`},
+ serverName: `"my-api"`,
+ expectedContent: []string{`"get_data"`, `"list_items"`},
+ // Regex: "tools" key whose value array starts with "*" (ignores whitespace/indentation).
+ // guard-policies "accept": ["*"] has a different key, so it is never matched.
+ unexpectedInServer: []string{`"tools"\s*:\s*\[\s*"\*"`},
},
{
name: "copilot - stdio mcp server with specific allowed tools",
@@ -240,9 +243,11 @@ mcp-servers:
Test workflow.
`,
- serverName: `"my-tool"`,
- expectedContent: []string{`"run_query"`, `"fetch_results"`},
- unexpectedInServer: []string{`"*"`},
+ serverName: `"my-tool"`,
+ expectedContent: []string{`"run_query"`, `"fetch_results"`},
+ // Regex: "tools" key whose value array starts with "*" (ignores whitespace/indentation).
+ // guard-policies "accept": ["*"] has a different key, so it is never matched.
+ unexpectedInServer: []string{`"tools"\s*:\s*\[\s*"\*"`},
},
{
name: "copilot - mcp server with no allowed field defaults to wildcard",
@@ -285,9 +290,11 @@ mcp-servers:
Test workflow.
`,
- serverName: `"my-api"`,
- expectedContent: []string{`"get_data"`, `"list_items"`},
- unexpectedInServer: []string{`"*"`},
+ serverName: `"my-api"`,
+ expectedContent: []string{`"get_data"`, `"list_items"`},
+ // Regex: "tools" key whose value array starts with "*" (ignores whitespace/indentation).
+ // guard-policies "accept": ["*"] has a different key, so it is never matched.
+ unexpectedInServer: []string{`"tools"\s*:\s*\[\s*"\*"`},
},
{
name: "claude - http mcp server with no allowed field has no tools filter",
@@ -355,10 +362,14 @@ Test workflow.
}
}
- for _, content := range tt.unexpectedInServer {
- if strings.Contains(serverBlock, content) {
- t.Errorf("Unexpected %q found in server block for %s.\nServer block:\n%s",
- content, tt.serverName, serverBlock)
+ for _, pattern := range tt.unexpectedInServer {
+ matched, err := regexp.MatchString(pattern, serverBlock)
+ if err != nil {
+ t.Fatalf("Invalid regex pattern %q: %v", pattern, err)
+ }
+ if matched {
+ t.Errorf("Unexpected pattern %q matched in server block for %s.\nServer block:\n%s",
+ pattern, tt.serverName, serverBlock)
}
}
})
@@ -487,7 +498,7 @@ This workflow tests that agentic-workflows uses the correct container in dev mod
}
// Verify binary mounts are NOT present in dev mode
- if strings.Contains(string(lockContent), `/opt/gh-aw:/opt/gh-aw:ro`) {
+ if strings.Contains(string(lockContent), `${GH_AW_HOME}:${GH_AW_HOME}:ro`) {
t.Error("Did not expect /opt/gh-aw mount in dev mode (binary is in image)")
}
if strings.Contains(string(lockContent), `/usr/bin/gh:/usr/bin/gh:ro`) {
diff --git a/pkg/workflow/mcp_config_refactor_test.go b/pkg/workflow/mcp_config_refactor_test.go
index 013c9f93ca5..b67e647b96b 100644
--- a/pkg/workflow/mcp_config_refactor_test.go
+++ b/pkg/workflow/mcp_config_refactor_test.go
@@ -115,10 +115,10 @@ func TestRenderAgenticWorkflowsMCPConfigWithOptions(t *testing.T) {
},
unexpectedContent: []string{
`--cmd`,
- `"entrypoint"`, // Not needed in dev mode - uses container's ENTRYPOINT
- `"entrypointArgs"`, // Not needed in dev mode - uses container's CMD
- `/opt/gh-aw:/opt/gh-aw:ro`, // Not needed in dev mode - binary is in image
- `/usr/bin/gh:/usr/bin/gh:ro`, // Not needed in dev mode - gh CLI is in image
+ `"entrypoint"`, // Not needed in dev mode - uses container's ENTRYPOINT
+ `"entrypointArgs"`, // Not needed in dev mode - uses container's CMD
+ `\${GH_AW_HOME}:\${GH_AW_HOME}:ro`, // Not needed in dev mode - binary is in image
+ `/usr/bin/gh:/usr/bin/gh:ro`, // Not needed in dev mode - gh CLI is in image
`${{ secrets.`,
`"command":`, // Should NOT use command - must use container
},
@@ -132,9 +132,9 @@ func TestRenderAgenticWorkflowsMCPConfigWithOptions(t *testing.T) {
`"agenticworkflows": {`,
`"type": "stdio"`,
`"container": "alpine:latest"`,
- `"entrypoint": "/opt/gh-aw/gh-aw"`,
+ `"entrypoint": "${GH_AW_HOME}/gh-aw"`,
`"entrypointArgs": ["mcp-server", "--validate-actor"]`,
- `"/opt/gh-aw:/opt/gh-aw:ro"`, // gh-aw binary mount (read-only)
+ `"\${GH_AW_HOME}:\${GH_AW_HOME}:ro"`, // gh-aw binary mount (read-only)
`"/usr/bin/gh:/usr/bin/gh:ro"`, // gh CLI binary mount (read-only)
`"\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"`, // workspace mount (read-write)
`"/tmp/gh-aw:/tmp/gh-aw:rw"`, // temp directory mount (read-write)
@@ -171,10 +171,10 @@ func TestRenderAgenticWorkflowsMCPConfigWithOptions(t *testing.T) {
`"type"`,
`\\${`,
`--cmd`,
- `"entrypoint"`, // Not needed in dev mode - uses container's ENTRYPOINT
- `"entrypointArgs"`, // Not needed in dev mode - uses container's CMD
- `/opt/gh-aw:/opt/gh-aw:ro`, // Not needed in dev mode - binary is in image
- `/usr/bin/gh:/usr/bin/gh:ro`, // Not needed in dev mode - gh CLI is in image
+ `"entrypoint"`, // Not needed in dev mode - uses container's ENTRYPOINT
+ `"entrypointArgs"`, // Not needed in dev mode - uses container's CMD
+ `\${GH_AW_HOME}:\${GH_AW_HOME}:ro`, // Not needed in dev mode - binary is in image
+ `/usr/bin/gh:/usr/bin/gh:ro`, // Not needed in dev mode - gh CLI is in image
// Verify GitHub expressions are NOT in the output (security fix)
`${{ secrets.`,
`"command":`, // Should NOT use command - must use container
@@ -231,7 +231,7 @@ func TestRenderSafeOutputsMCPConfigTOML(t *testing.T) {
unexpectedContent := []string{
`container = "node:lts-alpine"`,
`entrypoint = "node"`,
- `entrypointArgs = ["/opt/gh-aw/safeoutputs/mcp-server.cjs"]`,
+ `entrypointArgs = ["` + GhAwHome + `/safeoutputs/mcp-server.cjs"]`,
`mounts =`,
`env_vars =`,
`stdio`,
@@ -322,10 +322,10 @@ func TestRenderAgenticWorkflowsMCPConfigTOML(t *testing.T) {
},
unexpectedContent: []string{
`--cmd`,
- `entrypoint =`, // Not needed in dev mode - uses container's ENTRYPOINT
- `entrypointArgs =`, // Not needed in dev mode - uses container's CMD
- `/opt/gh-aw:/opt/gh-aw:ro`, // Not needed in dev mode
- `/usr/bin/gh:/usr/bin/gh:ro`, // Not needed in dev mode
+ `entrypoint =`, // Not needed in dev mode - uses container's ENTRYPOINT
+ `entrypointArgs =`, // Not needed in dev mode - uses container's CMD
+ `\${GH_AW_HOME}:\${GH_AW_HOME}:ro`, // Not needed in dev mode
+ `/usr/bin/gh:/usr/bin/gh:ro`, // Not needed in dev mode
},
},
{
@@ -334,9 +334,9 @@ func TestRenderAgenticWorkflowsMCPConfigTOML(t *testing.T) {
expectedContainer: `container = "alpine:latest"`,
shouldHaveEntrypoint: true,
expectedMounts: []string{
- `entrypoint = "/opt/gh-aw/gh-aw"`, // Entrypoint needed in release mode
+ `entrypoint = "` + GhAwHome + `/gh-aw"`, // Entrypoint needed in release mode
`entrypointArgs = ["mcp-server", "--validate-actor"]`, // EntrypointArgs needed in release mode with validate-actor flag
- `"/opt/gh-aw:/opt/gh-aw:ro"`, // gh-aw binary mount
+ `"\${GH_AW_HOME}:\${GH_AW_HOME}:ro"`, // gh-aw binary mount
`"/usr/bin/gh:/usr/bin/gh:ro"`, // gh CLI binary mount
`"\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"`, // workspace mount
`"/tmp/gh-aw:/tmp/gh-aw:rw"`, // temp directory mount
diff --git a/pkg/workflow/mcp_environment.go b/pkg/workflow/mcp_environment.go
index 98539ef1a49..454cf949b4a 100644
--- a/pkg/workflow/mcp_environment.go
+++ b/pkg/workflow/mcp_environment.go
@@ -16,7 +16,7 @@
// - Managing agentic-workflows GITHUB_TOKEN
//
// Environment variable categories:
-// - GitHub MCP: GITHUB_MCP_SERVER_TOKEN, GITHUB_MCP_LOCKDOWN
+// - GitHub MCP: GITHUB_MCP_SERVER_TOKEN, GITHUB_MCP_GUARD_MIN_INTEGRITY, GITHUB_MCP_GUARD_REPOS
// - Safe Outputs: GH_AW_SAFE_OUTPUTS_*, GH_AW_ASSETS_*
// - MCP Scripts: GH_AW_MCP_SCRIPTS_PORT, GH_AW_MCP_SCRIPTS_API_KEY
// - Serena: GH_AW_SERENA_PORT (local mode only)
@@ -79,13 +79,14 @@ func collectMCPEnvironmentVariables(tools map[string]any, mcpTools []string, wor
envVars["GITHUB_MCP_SERVER_TOKEN"] = effectiveToken
}
- // Add lockdown value if it's determined from step output.
- // Skip when a GitHub App is configured — in that case, the determine-automatic-lockdown
- // step is not generated, so there is no step output to reference.
- // Security: Pass step output through environment variable to prevent template injection
- // Convert "true"/"false" to "1"/"0" at the source to avoid shell conversion in templates
- if !hasGitHubLockdownExplicitlySet(githubTool) && !appConfigured {
- envVars["GITHUB_MCP_LOCKDOWN"] = "${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}"
+ // Add guard policy env vars if the determine-automatic-lockdown step will be generated.
+ // Skip when a GitHub App is configured or when guard policy is already explicitly set —
+ // in those cases, the determine-automatic-lockdown step is not generated.
+ // Security: Pass step outputs through environment variables to prevent template injection.
+ guardPoliciesExplicit := len(getGitHubGuardPolicies(githubTool)) > 0
+ if !guardPoliciesExplicit && !appConfigured {
+ envVars["GITHUB_MCP_GUARD_MIN_INTEGRITY"] = "${{ steps.determine-automatic-lockdown.outputs.min_integrity }}"
+ envVars["GITHUB_MCP_GUARD_REPOS"] = "${{ steps.determine-automatic-lockdown.outputs.repos }}"
}
}
diff --git a/pkg/workflow/mcp_github_config.go b/pkg/workflow/mcp_github_config.go
index bf1007a9439..5b08f663369 100644
--- a/pkg/workflow/mcp_github_config.go
+++ b/pkg/workflow/mcp_github_config.go
@@ -120,7 +120,7 @@ func getGitHubReadOnly(_ any) bool {
}
// getGitHubLockdown checks if lockdown mode is enabled for GitHub tool
-// Defaults to false (lockdown disabled)
+// Defaults to constants.DefaultGitHubLockdown (false)
func getGitHubLockdown(githubTool any) bool {
if toolConfig, ok := githubTool.(map[string]any); ok {
if lockdownSetting, exists := toolConfig["lockdown"]; exists {
@@ -129,7 +129,7 @@ func getGitHubLockdown(githubTool any) bool {
}
}
}
- return false // default to lockdown disabled
+ return constants.DefaultGitHubLockdown
}
// hasGitHubLockdownExplicitlySet checks if lockdown field is explicitly set in GitHub tool config
@@ -347,14 +347,42 @@ func transformRepoPattern(pattern string) string {
// from the workflow's GitHub guard-policy configuration. This uses the same derivation as
// deriveSafeOutputsGuardPolicyFromGitHub, ensuring that as guard policies are rolled out, only
// GitHub inputs are filtered while outputs to non-GitHub servers are not restricted.
-// Returns nil when no GitHub guard policies are configured or when workflowData is nil.
+//
+// Two cases produce a non-nil policy:
+// 1. Explicit guard policy — when repos/min-integrity are set on the GitHub tool, a write-sink
+// policy is derived from those settings (e.g. "private:myorg/myrepo").
+// 2. Auto-lockdown — when the GitHub tool is present without explicit guard policies and without
+// a GitHub App configured, auto-lockdown detection will set repos=all at runtime, so a
+// write-sink policy with accept=["*"] is returned to match that runtime behaviour.
+//
+// Returns nil when workflowData is nil, when no GitHub tool is present, or when a GitHub App is
+// configured (auto-lockdown is skipped for GitHub App tokens, which are already repo-scoped).
func deriveWriteSinkGuardPolicyFromWorkflow(workflowData *WorkflowData) map[string]any {
if workflowData == nil || workflowData.Tools == nil {
return nil
}
- if githubTool, hasGitHub := workflowData.Tools["github"]; hasGitHub {
- return deriveSafeOutputsGuardPolicyFromGitHub(githubTool)
+ githubTool, hasGitHub := workflowData.Tools["github"]
+ if !hasGitHub {
+ return nil
+ }
+
+ // Try to derive from explicit guard policy first
+ policy := deriveSafeOutputsGuardPolicyFromGitHub(githubTool)
+ if policy != nil {
+ return policy
+ }
+
+ // When no explicit guard policy is configured but automatic lockdown detection would run
+ // (GitHub tool present and not disabled, no GitHub App configured), return accept=["*"]
+ // because automatic lockdown always sets repos=all at runtime.
+ if githubTool != false && len(getGitHubGuardPolicies(githubTool)) == 0 && !hasGitHubApp(githubTool) {
+ return map[string]any{
+ "write-sink": map[string]any{
+ "accept": []string{"*"},
+ },
+ }
}
+
return nil
}
@@ -382,16 +410,16 @@ func getGitHubDockerImageVersion(githubTool any) string {
return githubDockerImageVersion
}
-// generateGitHubMCPLockdownDetectionStep generates a step to determine automatic lockdown mode
-// for GitHub MCP server based on repository visibility and token availability.
+// generateGitHubMCPLockdownDetectionStep generates a step to determine automatic guard policy
+// for GitHub MCP server based on repository visibility.
// This step is added when:
// - GitHub tool is enabled AND
-// - lockdown field is not explicitly specified in the workflow configuration AND
+// - guard policy (repos/min-integrity) is not fully configured in the workflow AND
// - tools.github.app is NOT configured (GitHub App tokens are already repo-scoped, so
-// automatic lockdown detection is unnecessary and skipped)
+// automatic guard policy detection is unnecessary and skipped)
//
-// Lockdown mode is automatically enabled for public repositories when any custom GitHub token
-// is configured (GH_AW_GITHUB_TOKEN, GH_AW_GITHUB_MCP_SERVER_TOKEN, or custom github-token).
+// For public repositories, the step automatically sets min-integrity to "approved" and
+// repos to "all" if they are not already configured.
func (c *Compiler) generateGitHubMCPLockdownDetectionStep(yaml *strings.Builder, data *WorkflowData) {
// Check if GitHub tool is present
githubTool, hasGitHub := data.Tools["github"]
@@ -399,22 +427,23 @@ func (c *Compiler) generateGitHubMCPLockdownDetectionStep(yaml *strings.Builder,
return
}
- // Check if lockdown is already explicitly set
- if hasGitHubLockdownExplicitlySet(githubTool) {
- githubConfigLog.Print("Lockdown explicitly set in workflow, skipping automatic lockdown determination")
+ // Skip when guard policy is already fully configured in the workflow.
+ // The step is only needed to auto-configure guard policies for public repos.
+ if len(getGitHubGuardPolicies(githubTool)) > 0 {
+ githubConfigLog.Print("Guard policy already configured in workflow, skipping automatic guard policy determination")
return
}
- // Skip automatic lockdown detection when a GitHub App is configured.
+ // Skip automatic guard policy detection when a GitHub App is configured.
// GitHub App tokens are already scoped to specific repositories, so automatic
- // lockdown detection is not needed — the token's access is inherently bounded
+ // guard policy detection is not needed — the token's access is inherently bounded
// by the app installation and the listed repositories.
if hasGitHubApp(githubTool) {
- githubConfigLog.Print("GitHub App configured, skipping automatic lockdown determination (app tokens are already repo-scoped)")
+ githubConfigLog.Print("GitHub App configured, skipping automatic guard policy determination (app tokens are already repo-scoped)")
return
}
- githubConfigLog.Print("Generating automatic lockdown determination step for GitHub MCP server")
+ githubConfigLog.Print("Generating automatic guard policy determination step for GitHub MCP server")
// Resolve the latest version of actions/github-script
actionRepo := "actions/github-script"
@@ -427,8 +456,18 @@ func (c *Compiler) generateGitHubMCPLockdownDetectionStep(yaml *strings.Builder,
pinnedAction = fmt.Sprintf("%s@%s", actionRepo, actionVersion)
}
- // Extract custom github-token if present
- customToken := getGitHubToken(githubTool)
+ // Extract current guard policy configuration to pass as env vars so the step can
+ // detect whether each field is already configured and avoid overriding it.
+ configuredMinIntegrity := ""
+ configuredRepos := ""
+ if toolConfig, ok := githubTool.(map[string]any); ok {
+ if v, exists := toolConfig["min-integrity"]; exists {
+ configuredMinIntegrity = fmt.Sprintf("%v", v)
+ }
+ if v, exists := toolConfig["repos"]; exists {
+ configuredRepos = fmt.Sprintf("%v", v)
+ }
+ }
// Generate the step using the determine_automatic_lockdown.cjs action
yaml.WriteString(" - name: Determine automatic lockdown mode for GitHub MCP Server\n")
@@ -437,12 +476,15 @@ func (c *Compiler) generateGitHubMCPLockdownDetectionStep(yaml *strings.Builder,
yaml.WriteString(" env:\n")
yaml.WriteString(" GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}\n")
yaml.WriteString(" GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}\n")
- if customToken != "" {
- fmt.Fprintf(yaml, " CUSTOM_GITHUB_TOKEN: %s\n", customToken)
+ if configuredMinIntegrity != "" {
+ fmt.Fprintf(yaml, " GH_AW_GITHUB_MIN_INTEGRITY: %s\n", configuredMinIntegrity)
+ }
+ if configuredRepos != "" {
+ fmt.Fprintf(yaml, " GH_AW_GITHUB_REPOS: %s\n", configuredRepos)
}
yaml.WriteString(" with:\n")
yaml.WriteString(" script: |\n")
- yaml.WriteString(" const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');\n")
+ yaml.WriteString(" const determineAutomaticLockdown = require(" + JsRequireGhAw("actions/determine_automatic_lockdown.cjs") + ");\n")
yaml.WriteString(" await determineAutomaticLockdown(github, context, core);\n")
}
diff --git a/pkg/workflow/mcp_renderer.go b/pkg/workflow/mcp_renderer.go
index 9a2d1bfbd5e..30a8f97f4b1 100644
--- a/pkg/workflow/mcp_renderer.go
+++ b/pkg/workflow/mcp_renderer.go
@@ -206,7 +206,7 @@ func RenderJSONMCPConfig(
delimiter := GenerateHeredocDelimiter("MCP_CONFIG")
// Write the configuration to the YAML output
- yaml.WriteString(" cat << " + delimiter + " | bash /opt/gh-aw/actions/start_mcp_gateway.sh\n")
+ yaml.WriteString(" cat << " + delimiter + " | bash " + GhAwHome + "/actions/start_mcp_gateway.sh\n")
yaml.WriteString(generatedConfig)
yaml.WriteString(" " + delimiter + "\n")
diff --git a/pkg/workflow/mcp_renderer_builtin.go b/pkg/workflow/mcp_renderer_builtin.go
index 9bef8563614..97be108f8be 100644
--- a/pkg/workflow/mcp_renderer_builtin.go
+++ b/pkg/workflow/mcp_renderer_builtin.go
@@ -170,17 +170,13 @@ func (r *MCPConfigRendererUnified) renderSafeOutputsTOML(yaml *strings.Builder,
yaml.WriteString(" [mcp_servers." + constants.SafeOutputsMCPServerID.String() + ".headers]\n")
yaml.WriteString(" Authorization = \"$GH_AW_SAFE_OUTPUTS_API_KEY\"\n")
- // Check if GitHub tool has guard-policies configured
+ // Check if GitHub tool has guard-policies configured (or auto-lockdown will run)
// If so, generate a linked write-sink guard-policy for safeoutputs
- if workflowData != nil && workflowData.Tools != nil {
- if githubTool, hasGitHub := workflowData.Tools["github"]; hasGitHub {
- guardPolicies := deriveSafeOutputsGuardPolicyFromGitHub(githubTool)
- if len(guardPolicies) > 0 {
- mcpRendererLog.Print("Adding guard-policies to safeoutputs TOML (derived from GitHub guard-policy)")
- // Render guard-policies in TOML format
- renderGuardPoliciesToml(yaml, guardPolicies, constants.SafeOutputsMCPServerID.String())
- }
- }
+ guardPolicies := deriveWriteSinkGuardPolicyFromWorkflow(workflowData)
+ if len(guardPolicies) > 0 {
+ mcpRendererLog.Print("Adding guard-policies to safeoutputs TOML (derived from GitHub guard-policy or auto-lockdown detection)")
+ // Render guard-policies in TOML format
+ renderGuardPoliciesToml(yaml, guardPolicies, constants.SafeOutputsMCPServerID.String())
}
}
@@ -266,7 +262,7 @@ func (r *MCPConfigRendererUnified) renderAgenticWorkflowsTOML(yaml *strings.Buil
mounts = []string{constants.DefaultWorkspaceMount, constants.DefaultTmpGhAwMount}
} else {
// Release mode: Use minimal Alpine image with mounted binaries
- entrypoint = "/opt/gh-aw/gh-aw"
+ entrypoint = GhAwHome + "/gh-aw"
entrypointArgs = []string{"mcp-server", "--validate-actor"}
// Mount gh-aw binary, gh CLI binary, workspace, and temp directory
mounts = []string{constants.DefaultGhAwMount, constants.DefaultGhBinaryMount, constants.DefaultWorkspaceMount, constants.DefaultTmpGhAwMount}
diff --git a/pkg/workflow/mcp_renderer_github.go b/pkg/workflow/mcp_renderer_github.go
index 2fffd20ae89..8b1235bf8b2 100644
--- a/pkg/workflow/mcp_renderer_github.go
+++ b/pkg/workflow/mcp_renderer_github.go
@@ -16,24 +16,19 @@ func (r *MCPConfigRendererUnified) RenderGitHubMCP(yaml *strings.Builder, github
githubType := getGitHubType(githubTool)
readOnly := getGitHubReadOnly(githubTool)
- // Get lockdown value - use detected value if lockdown wasn't explicitly set
+ // Get explicit lockdown value (only used when lockdown is explicitly configured)
lockdown := getGitHubLockdown(githubTool)
- // Check if automatic lockdown determination step will be generated.
- // The step is skipped when lockdown is explicitly set, or when a GitHub App is configured
- // (app tokens are already repo-scoped, so automatic lockdown detection is not needed).
- shouldUseStepOutput := !hasGitHubLockdownExplicitlySet(githubTool) && !hasGitHubApp(githubTool)
-
- if shouldUseStepOutput {
- // Use the detected lockdown value from the step output
- // This will be evaluated at runtime based on repository visibility
- lockdown = true // This is a placeholder - actual value comes from step output
- }
+ // Guard policies from step: automatically applied for public repositories when no explicit
+ // guard policy is configured and no GitHub App token is in use.
+ // The determine-automatic-lockdown step outputs min_integrity and repos for public repos.
+ explicitGuardPolicies := getGitHubGuardPolicies(githubTool)
+ shouldUseStepOutputForGuardPolicy := len(explicitGuardPolicies) == 0 && !hasGitHubApp(githubTool)
toolsets := getGitHubToolsets(githubTool)
- mcpRendererLog.Printf("Rendering GitHub MCP: type=%s, read_only=%t, lockdown=%t (explicit=%t, use_step=%t), toolsets=%v, format=%s",
- githubType, readOnly, lockdown, hasGitHubLockdownExplicitlySet(githubTool), shouldUseStepOutput, toolsets, r.options.Format)
+ mcpRendererLog.Printf("Rendering GitHub MCP: type=%s, read_only=%t, lockdown=%t (explicit=%t), guard_from_step=%t, toolsets=%v, format=%s",
+ githubType, readOnly, lockdown, hasGitHubLockdownExplicitlySet(githubTool), shouldUseStepOutputForGuardPolicy, toolsets, r.options.Format)
if r.options.Format == "toml" {
r.renderGitHubTOML(yaml, githubTool, workflowData)
@@ -53,15 +48,16 @@ func (r *MCPConfigRendererUnified) RenderGitHubMCP(yaml *strings.Builder, github
}
RenderGitHubMCPRemoteConfig(yaml, GitHubMCPRemoteOptions{
- ReadOnly: readOnly,
- Lockdown: lockdown,
- LockdownFromStep: shouldUseStepOutput,
- Toolsets: toolsets,
- AuthorizationValue: authValue,
- IncludeToolsField: r.options.IncludeCopilotFields,
- AllowedTools: getGitHubAllowedTools(githubTool),
- IncludeEnvSection: r.options.IncludeCopilotFields,
- GuardPolicies: getGitHubGuardPolicies(githubTool),
+ ReadOnly: readOnly,
+ Lockdown: lockdown,
+ LockdownFromStep: false,
+ GuardPoliciesFromStep: shouldUseStepOutputForGuardPolicy,
+ Toolsets: toolsets,
+ AuthorizationValue: authValue,
+ IncludeToolsField: r.options.IncludeCopilotFields,
+ AllowedTools: getGitHubAllowedTools(githubTool),
+ IncludeEnvSection: r.options.IncludeCopilotFields,
+ GuardPolicies: explicitGuardPolicies,
})
} else {
// Local mode - use Docker-based GitHub MCP server (default)
@@ -70,17 +66,18 @@ func (r *MCPConfigRendererUnified) RenderGitHubMCP(yaml *strings.Builder, github
mounts := getGitHubMounts(githubTool)
RenderGitHubMCPDockerConfig(yaml, GitHubMCPDockerOptions{
- ReadOnly: readOnly,
- Lockdown: lockdown,
- LockdownFromStep: shouldUseStepOutput,
- Toolsets: toolsets,
- DockerImageVersion: githubDockerImageVersion,
- CustomArgs: customArgs,
- Mounts: mounts,
- IncludeTypeField: r.options.IncludeCopilotFields,
- AllowedTools: getGitHubAllowedTools(githubTool),
- EffectiveToken: "", // Token passed via env
- GuardPolicies: getGitHubGuardPolicies(githubTool),
+ ReadOnly: readOnly,
+ Lockdown: lockdown,
+ LockdownFromStep: false,
+ GuardPoliciesFromStep: shouldUseStepOutputForGuardPolicy,
+ Toolsets: toolsets,
+ DockerImageVersion: githubDockerImageVersion,
+ CustomArgs: customArgs,
+ Mounts: mounts,
+ IncludeTypeField: r.options.IncludeCopilotFields,
+ AllowedTools: getGitHubAllowedTools(githubTool),
+ EffectiveToken: "", // Token passed via env
+ GuardPolicies: explicitGuardPolicies,
})
}
@@ -98,6 +95,8 @@ func (r *MCPConfigRendererUnified) renderGitHubTOML(yaml *strings.Builder, githu
lockdown := getGitHubLockdown(githubTool)
toolsets := getGitHubToolsets(githubTool)
+ mcpRendererLog.Printf("Rendering GitHub MCP TOML: type=%s, read_only=%t, lockdown=%t, toolsets=%s", githubType, readOnly, lockdown, toolsets)
+
yaml.WriteString(" \n")
yaml.WriteString(" [mcp_servers.github]\n")
@@ -220,6 +219,8 @@ func (r *MCPConfigRendererUnified) renderGitHubTOML(yaml *strings.Builder, githu
// - yaml: The string builder for YAML output
// - options: GitHub MCP Docker rendering options
func RenderGitHubMCPDockerConfig(yaml *strings.Builder, options GitHubMCPDockerOptions) {
+ mcpRendererLog.Printf("Rendering GitHub MCP Docker config: image=%s, read_only=%t, lockdown=%t", options.DockerImageVersion, options.ReadOnly, options.Lockdown)
+
// Add type field if needed (Copilot requires this, Claude doesn't)
// Per MCP Gateway Specification v1.0.0 section 4.1.2, use "stdio" for containerized servers
if options.IncludeTypeField {
@@ -286,13 +287,8 @@ func RenderGitHubMCPDockerConfig(yaml *strings.Builder, options GitHubMCPDockerO
envVars["GITHUB_READ_ONLY"] = "1"
}
- // GitHub lockdown mode
- if options.LockdownFromStep {
- // Security: Use environment variable instead of template expression to prevent template injection
- // The GITHUB_MCP_LOCKDOWN env var is set in Start MCP Gateway step from step output
- // Value is already converted to "1" or "0" in the environment variable
- envVars["GITHUB_LOCKDOWN_MODE"] = "$GITHUB_MCP_LOCKDOWN"
- } else if options.Lockdown {
+ // GitHub lockdown mode (only when explicitly configured)
+ if options.Lockdown {
// Use explicit lockdown value from configuration
envVars["GITHUB_LOCKDOWN_MODE"] = "1"
}
@@ -313,9 +309,23 @@ func RenderGitHubMCPDockerConfig(yaml *strings.Builder, options GitHubMCPDockerO
}
// Close env section, with trailing comma if guard-policies follows
- if len(options.GuardPolicies) > 0 {
+ hasGuardPolicies := len(options.GuardPolicies) > 0 || options.GuardPoliciesFromStep
+ if hasGuardPolicies {
yaml.WriteString(" },\n")
- renderGuardPoliciesJSON(yaml, options.GuardPolicies, " ")
+ if options.GuardPoliciesFromStep {
+ // Render guard-policies with env var refs resolved at runtime from step outputs
+ // GITHUB_MCP_GUARD_MIN_INTEGRITY and GITHUB_MCP_GUARD_REPOS are set in Start MCP
+ // Gateway step from the determine-automatic-lockdown step outputs. They are
+ // non-empty only for public repositories.
+ renderGuardPoliciesJSON(yaml, map[string]any{
+ "allow-only": map[string]any{
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS",
+ },
+ }, " ")
+ } else {
+ renderGuardPoliciesJSON(yaml, options.GuardPolicies, " ")
+ }
} else {
yaml.WriteString(" }\n")
}
@@ -328,6 +338,8 @@ func RenderGitHubMCPDockerConfig(yaml *strings.Builder, options GitHubMCPDockerO
// - yaml: The string builder for YAML output
// - options: GitHub MCP remote rendering options
func RenderGitHubMCPRemoteConfig(yaml *strings.Builder, options GitHubMCPRemoteOptions) {
+ mcpRendererLog.Printf("Rendering GitHub MCP remote config: read_only=%t, lockdown=%t, toolsets=%s", options.ReadOnly, options.Lockdown, options.Toolsets)
+
// Remote mode - use hosted GitHub MCP server
yaml.WriteString(" \"type\": \"http\",\n")
yaml.WriteString(" \"url\": \"https://api.githubcopilot.com/mcp/\",\n")
@@ -342,12 +354,8 @@ func RenderGitHubMCPRemoteConfig(yaml *strings.Builder, options GitHubMCPRemoteO
headers["X-MCP-Readonly"] = "true"
}
- // Add X-MCP-Lockdown header if lockdown mode is enabled
- if options.LockdownFromStep {
- // Security: Use environment variable instead of template expression to prevent template injection
- // The GITHUB_MCP_LOCKDOWN env var contains "1" or "0", convert to "true" or "false" for header
- headers["X-MCP-Lockdown"] = "$([ \"$GITHUB_MCP_LOCKDOWN\" = \"1\" ] && echo true || echo false)"
- } else if options.Lockdown {
+ // Add X-MCP-Lockdown header only when explicitly configured
+ if options.Lockdown {
// Use explicit lockdown value from configuration
headers["X-MCP-Lockdown"] = "true"
}
@@ -360,8 +368,11 @@ func RenderGitHubMCPRemoteConfig(yaml *strings.Builder, options GitHubMCPRemoteO
// Write headers using helper
writeHeadersToYAML(yaml, headers, " ")
+ // Determine if guard-policies section follows (explicit or from step)
+ hasGuardPolicies := len(options.GuardPolicies) > 0 || options.GuardPoliciesFromStep
+
// Close headers section
- if options.IncludeToolsField || options.IncludeEnvSection || len(options.GuardPolicies) > 0 {
+ if options.IncludeToolsField || options.IncludeEnvSection || hasGuardPolicies {
yaml.WriteString(" },\n")
} else {
yaml.WriteString(" }\n")
@@ -381,7 +392,7 @@ func RenderGitHubMCPRemoteConfig(yaml *strings.Builder, options GitHubMCPRemoteO
}
yaml.WriteString("\n")
}
- if options.IncludeEnvSection || len(options.GuardPolicies) > 0 {
+ if options.IncludeEnvSection || hasGuardPolicies {
yaml.WriteString(" ],\n")
} else {
yaml.WriteString(" ]\n")
@@ -397,15 +408,26 @@ func RenderGitHubMCPRemoteConfig(yaml *strings.Builder, options GitHubMCPRemoteO
// which matches the format expected by github-mcp-server for GITHUB_HOST.
yaml.WriteString(" \"GITHUB_HOST\": \"\\${GITHUB_SERVER_URL}\"\n")
// Close env section, with trailing comma if guard-policies follows
- if len(options.GuardPolicies) > 0 {
+ if hasGuardPolicies {
yaml.WriteString(" },\n")
} else {
yaml.WriteString(" }\n")
}
}
- // Add guard-policies if configured
- if len(options.GuardPolicies) > 0 {
+ // Add guard-policies if configured or from step
+ if options.GuardPoliciesFromStep {
+ // Render guard-policies with env var refs resolved at runtime from step outputs
+ // GITHUB_MCP_GUARD_MIN_INTEGRITY and GITHUB_MCP_GUARD_REPOS are set in Start MCP
+ // Gateway step from the determine-automatic-lockdown step outputs. They are
+ // non-empty only for public repositories.
+ renderGuardPoliciesJSON(yaml, map[string]any{
+ "allow-only": map[string]any{
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS",
+ },
+ }, " ")
+ } else if len(options.GuardPolicies) > 0 {
renderGuardPoliciesJSON(yaml, options.GuardPolicies, " ")
}
}
diff --git a/pkg/workflow/mcp_renderer_helpers.go b/pkg/workflow/mcp_renderer_helpers.go
new file mode 100644
index 00000000000..65c47a46462
--- /dev/null
+++ b/pkg/workflow/mcp_renderer_helpers.go
@@ -0,0 +1,63 @@
+package workflow
+
+import "strings"
+
+// buildMCPRendererFactory creates a factory function for MCPConfigRendererUnified instances.
+// The returned function accepts isLast as a parameter and creates a renderer with engine-specific
+// options derived from the provided parameters and workflowData at call time.
+func buildMCPRendererFactory(workflowData *WorkflowData, format string, includeCopilotFields, inlineArgs bool) func(bool) *MCPConfigRendererUnified {
+ return func(isLast bool) *MCPConfigRendererUnified {
+ return NewMCPConfigRenderer(MCPRendererOptions{
+ IncludeCopilotFields: includeCopilotFields,
+ InlineArgs: inlineArgs,
+ Format: format,
+ IsLast: isLast,
+ ActionMode: GetActionModeFromWorkflowData(workflowData),
+ WriteSinkGuardPolicies: deriveWriteSinkGuardPolicyFromWorkflow(workflowData),
+ })
+ }
+}
+
+// buildStandardJSONMCPRenderers constructs MCPToolRenderers with the standard rendering callbacks
+// shared across JSON-format engines (Claude, Gemini, Copilot, Codex gateway).
+//
+// All eight standard tool callbacks (GitHub, Playwright, Serena, CacheMemory, AgenticWorkflows,
+// SafeOutputs, MCPScripts, WebFetch) are wired to the corresponding unified renderer methods
+// via createRenderer. Cache-memory is always a no-op for these engines.
+//
+// webFetchIncludeTools controls whether the web-fetch server includes a tools field:
+// set to true for Copilot (which uses inline args) and false for all other engines.
+//
+// renderCustom is the engine-specific handler for custom MCP tool configuration entries.
+func buildStandardJSONMCPRenderers(
+ workflowData *WorkflowData,
+ createRenderer func(bool) *MCPConfigRendererUnified,
+ webFetchIncludeTools bool,
+ renderCustom RenderCustomMCPToolConfigHandler,
+) MCPToolRenderers {
+ return MCPToolRenderers{
+ RenderGitHub: func(yaml *strings.Builder, githubTool any, isLast bool, workflowData *WorkflowData) {
+ createRenderer(isLast).RenderGitHubMCP(yaml, githubTool, workflowData)
+ },
+ RenderPlaywright: func(yaml *strings.Builder, playwrightTool any, isLast bool) {
+ createRenderer(isLast).RenderPlaywrightMCP(yaml, playwrightTool)
+ },
+ RenderSerena: func(yaml *strings.Builder, serenaTool any, isLast bool) {
+ createRenderer(isLast).RenderSerenaMCP(yaml, serenaTool)
+ },
+ RenderCacheMemory: noOpCacheMemoryRenderer,
+ RenderAgenticWorkflows: func(yaml *strings.Builder, isLast bool) {
+ createRenderer(isLast).RenderAgenticWorkflowsMCP(yaml)
+ },
+ RenderSafeOutputs: func(yaml *strings.Builder, isLast bool, workflowData *WorkflowData) {
+ createRenderer(isLast).RenderSafeOutputsMCP(yaml, workflowData)
+ },
+ RenderMCPScripts: func(yaml *strings.Builder, mcpScripts *MCPScriptsConfig, isLast bool) {
+ createRenderer(isLast).RenderMCPScriptsMCP(yaml, mcpScripts, workflowData)
+ },
+ RenderWebFetch: func(yaml *strings.Builder, isLast bool) {
+ renderMCPFetchServerConfig(yaml, "json", " ", isLast, webFetchIncludeTools, deriveWriteSinkGuardPolicyFromWorkflow(workflowData))
+ },
+ RenderCustomMCPConfig: renderCustom,
+ }
+}
diff --git a/pkg/workflow/mcp_renderer_test.go b/pkg/workflow/mcp_renderer_test.go
index 3a6432b6401..cac354e62f6 100644
--- a/pkg/workflow/mcp_renderer_test.go
+++ b/pkg/workflow/mcp_renderer_test.go
@@ -200,7 +200,7 @@ func TestRenderAgenticWorkflowsMCP_JSON_Copilot(t *testing.T) {
t.Error("Did not expect entrypointArgs field in dev mode (uses container's CMD)")
}
// In dev mode, should NOT have binary mounts
- if strings.Contains(output, `/opt/gh-aw:/opt/gh-aw:ro`) {
+ if strings.Contains(output, `${GH_AW_HOME}:${GH_AW_HOME}:ro`) {
t.Error("Did not expect /opt/gh-aw mount in dev mode (binary is in image)")
}
if strings.Contains(output, `/usr/bin/gh:/usr/bin/gh:ro`) {
@@ -274,7 +274,7 @@ func TestRenderAgenticWorkflowsMCP_TOML(t *testing.T) {
t.Error("Did not expect entrypointArgs field in dev mode (uses container's CMD)")
}
// In dev mode, should NOT have binary mounts
- if strings.Contains(output, `/opt/gh-aw:/opt/gh-aw:ro`) {
+ if strings.Contains(output, `${GH_AW_HOME}:${GH_AW_HOME}:ro`) {
t.Error("Did not expect /opt/gh-aw mount in dev mode (binary is in image)")
}
if strings.Contains(output, `/usr/bin/gh:/usr/bin/gh:ro`) {
diff --git a/pkg/workflow/mcp_renderer_types.go b/pkg/workflow/mcp_renderer_types.go
index 0bcece2e40d..97a4803661a 100644
--- a/pkg/workflow/mcp_renderer_types.go
+++ b/pkg/workflow/mcp_renderer_types.go
@@ -67,6 +67,9 @@ type GitHubMCPDockerOptions struct {
Lockdown bool
// LockdownFromStep indicates if lockdown value should be read from step output
LockdownFromStep bool
+ // GuardPoliciesFromStep indicates if guard policy values should be read from step outputs
+ // (GITHUB_MCP_GUARD_MIN_INTEGRITY and GITHUB_MCP_GUARD_REPOS env vars)
+ GuardPoliciesFromStep bool
// Toolsets specifies the GitHub toolsets to enable
Toolsets string
// DockerImageVersion specifies the GitHub MCP server Docker image version
@@ -93,6 +96,9 @@ type GitHubMCPRemoteOptions struct {
Lockdown bool
// LockdownFromStep indicates if lockdown value should be read from step output
LockdownFromStep bool
+ // GuardPoliciesFromStep indicates if guard policy values should be read from step outputs
+ // (GITHUB_MCP_GUARD_MIN_INTEGRITY and GITHUB_MCP_GUARD_REPOS env vars)
+ GuardPoliciesFromStep bool
// Toolsets specifies the GitHub toolsets to enable
Toolsets string
// AuthorizationValue is the value for the Authorization header
diff --git a/pkg/workflow/mcp_scripts_generator.go b/pkg/workflow/mcp_scripts_generator.go
index b109033d4ec..780b90c8d9c 100644
--- a/pkg/workflow/mcp_scripts_generator.go
+++ b/pkg/workflow/mcp_scripts_generator.go
@@ -164,7 +164,7 @@ const apiKey = process.env.GH_AW_MCP_SCRIPTS_API_KEY || "";
startHttpServer(configPath, {
port: port,
stateless: true,
- logDir: "/opt/gh-aw/mcp-scripts/logs"
+ logDir: ` + GhAwHomeJS + ` + "/mcp-scripts/logs"
}).catch(error => {
console.error("Failed to start mcp-scripts HTTP server:", error);
process.exit(1);
diff --git a/pkg/workflow/mcp_scripts_generator_test.go b/pkg/workflow/mcp_scripts_generator_test.go
index 45de3579500..c5b15f510ec 100644
--- a/pkg/workflow/mcp_scripts_generator_test.go
+++ b/pkg/workflow/mcp_scripts_generator_test.go
@@ -77,7 +77,7 @@ func TestGenerateMCPScriptsMCPServerScript(t *testing.T) {
t.Error("Script should reference tools.json configuration file")
}
- if !strings.Contains(script, "/opt/gh-aw/mcp-scripts/logs") {
+ if !strings.Contains(script, "/mcp-scripts/logs") {
t.Error("Script should specify log directory")
}
diff --git a/pkg/workflow/mcp_scripts_http_codex_test.go b/pkg/workflow/mcp_scripts_http_codex_test.go
index b4ec8731e93..0a2c4b59c17 100644
--- a/pkg/workflow/mcp_scripts_http_codex_test.go
+++ b/pkg/workflow/mcp_scripts_http_codex_test.go
@@ -90,7 +90,7 @@ Test mcp-scripts HTTP transport for Codex
t.Error("Codex config should not use stdio transport (command = 'node'), should use HTTP")
}
- if strings.Contains(codexConfigSection, `args = [`) && strings.Contains(codexConfigSection, `/opt/gh-aw/mcp-scripts/mcp-server.cjs`) {
+ if strings.Contains(codexConfigSection, `args = [`) && strings.Contains(codexConfigSection, `${GH_AW_HOME}/mcp-scripts/mcp-server.cjs`) {
t.Error("Codex config should not use stdio transport with mcp-server.cjs args, should use HTTP")
}
diff --git a/pkg/workflow/mcp_scripts_mode_test.go b/pkg/workflow/mcp_scripts_mode_test.go
index 832550141c8..7f3472975b2 100644
--- a/pkg/workflow/mcp_scripts_mode_test.go
+++ b/pkg/workflow/mcp_scripts_mode_test.go
@@ -117,7 +117,7 @@ Test mcp-scripts HTTP mode
// extractMCPServerEntryPoint extracts the mcp-server.cjs entry point script from the YAML
func extractMCPServerEntryPoint(yamlStr string) string {
// Find the mcp-server.cjs section
- start := strings.Index(yamlStr, "cat > /opt/gh-aw/mcp-scripts/mcp-server.cjs")
+ start := strings.Index(yamlStr, "cat > ${GH_AW_HOME}/mcp-scripts/mcp-server.cjs")
if start == -1 {
return ""
}
diff --git a/pkg/workflow/mcp_scripts_parser.go b/pkg/workflow/mcp_scripts_parser.go
index cfdf94b49ec..d634618ac92 100644
--- a/pkg/workflow/mcp_scripts_parser.go
+++ b/pkg/workflow/mcp_scripts_parser.go
@@ -3,22 +3,12 @@ package workflow
import (
"encoding/json"
"fmt"
- "math"
"github.com/github/gh-aw/pkg/logger"
)
var mcpScriptsLog = logger.New("workflow:mcp_scripts")
-// safeUint64ToIntForTimeout safely converts uint64 to int for timeout values
-// Returns 0 (which signals to use engine defaults) if overflow would occur
-func safeUint64ToIntForTimeout(u uint64) int {
- if u > math.MaxInt {
- return 0 // Return 0 (engine default) if value would overflow
- }
- return int(u)
-}
-
// MCPScriptsConfig holds the configuration for mcp-scripts custom tools
type MCPScriptsConfig struct {
Mode string // Transport mode: "http" (default) or "stdio"
@@ -52,7 +42,7 @@ const (
)
// MCPScriptsDirectory is the directory where mcp-scripts files are generated
-const MCPScriptsDirectory = "/opt/gh-aw/mcp-scripts"
+const MCPScriptsDirectory = GhAwHome + "/mcp-scripts"
// HasMCPScripts checks if mcp-scripts are configured
func HasMCPScripts(mcpScripts *MCPScriptsConfig) bool {
@@ -185,7 +175,7 @@ func parseMCPScriptsMap(mcpScriptsMap map[string]any) (*MCPScriptsConfig, bool)
case int:
toolConfig.Timeout = t
case uint64:
- toolConfig.Timeout = safeUint64ToIntForTimeout(t) // Safe conversion to prevent overflow (alert #414)
+ toolConfig.Timeout = safeUint64ToInt(t) // Safe conversion to prevent overflow (alert #414)
case float64:
toolConfig.Timeout = int(t)
case string:
@@ -356,7 +346,7 @@ func (c *Compiler) mergeMCPScripts(main *MCPScriptsConfig, importedConfigs []str
case int:
toolConfig.Timeout = t
case uint64:
- toolConfig.Timeout = safeUint64ToIntForTimeout(t) // Safe conversion to prevent overflow (alert #413)
+ toolConfig.Timeout = safeUint64ToInt(t) // Safe conversion to prevent overflow (alert #413)
case float64:
toolConfig.Timeout = int(t)
case string:
diff --git a/pkg/workflow/mcp_setup_generator.go b/pkg/workflow/mcp_setup_generator.go
index 6f17d1717cc..568a14d877f 100644
--- a/pkg/workflow/mcp_setup_generator.go
+++ b/pkg/workflow/mcp_setup_generator.go
@@ -184,12 +184,12 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any,
yaml.WriteString(" fi\n")
yaml.WriteString(" gh aw --version\n")
yaml.WriteString(" # Copy the gh-aw binary to /opt/gh-aw for MCP server containerization\n")
- yaml.WriteString(" mkdir -p /opt/gh-aw\n")
+ yaml.WriteString(" mkdir -p " + GhAwHome + "\n")
yaml.WriteString(" GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)\n")
yaml.WriteString(" if [ -n \"$GH_AW_BIN\" ] && [ -f \"$GH_AW_BIN\" ]; then\n")
- yaml.WriteString(" cp \"$GH_AW_BIN\" /opt/gh-aw/gh-aw\n")
- yaml.WriteString(" chmod +x /opt/gh-aw/gh-aw\n")
- yaml.WriteString(" echo \"Copied gh-aw binary to /opt/gh-aw/gh-aw\"\n")
+ yaml.WriteString(" cp \"$GH_AW_BIN\" " + GhAwHome + "/gh-aw\n")
+ yaml.WriteString(" chmod +x " + GhAwHome + "/gh-aw\n")
+ yaml.WriteString(" echo \"Copied gh-aw binary to " + GhAwHome + "/gh-aw\"\n")
yaml.WriteString(" else\n")
yaml.WriteString(" echo \"::error::Failed to find gh-aw binary for MCP server\"\n")
yaml.WriteString(" exit 1\n")
@@ -206,30 +206,36 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any,
// AND exceeds 21,000 characters total.
yaml.WriteString(" - name: Write Safe Outputs Config\n")
yaml.WriteString(" run: |\n")
- yaml.WriteString(" mkdir -p /opt/gh-aw/safeoutputs\n")
+ yaml.WriteString(" mkdir -p " + GhAwHome + "/safeoutputs\n")
yaml.WriteString(" mkdir -p /tmp/gh-aw/safeoutputs\n")
yaml.WriteString(" mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs\n")
// Write the safe-outputs configuration to config.json
delimiter := GenerateHeredocDelimiter("SAFE_OUTPUTS_CONFIG")
if safeOutputConfig != "" {
- yaml.WriteString(" cat > /opt/gh-aw/safeoutputs/config.json << '" + delimiter + "'\n")
+ yaml.WriteString(" cat > " + GhAwHome + "/safeoutputs/config.json << '" + delimiter + "'\n")
yaml.WriteString(" " + safeOutputConfig + "\n")
yaml.WriteString(" " + delimiter + "\n")
}
- // Step 1b: Write tools.json and validation.json in a SEPARATE step.
- // These files can be very large (e.g. 20KB+ when toolsets: [all] is configured)
- // but contain only static content with no GitHub Actions template expressions.
- // Keeping them in a separate run: block ensures they never combine with
+ // Step 1b: Write tools_meta.json and validation.json in a SEPARATE step.
+ // tools_meta.json replaces the large inlined tools.json heredoc: it contains
+ // only the workflow-specific customisations (description suffixes, repo params,
+ // dynamic tools). At runtime, generate_safe_outputs_tools.cjs combines this
+ // with the source safe_outputs_tools.json from the actions folder to produce
+ // the final /opt/gh-aw/safeoutputs/tools.json.
+ //
+ // Keeping this in a separate run: block ensures it never combines with
// expression-containing content to exceed GitHub Actions' 21,000-character limit.
- // Generate and write the filtered tools.json file
- filteredToolsJSON, err := generateFilteredToolsJSON(workflowData, c.markdownPath)
+ // Generate tools_meta.json: small file with description suffixes, repo params,
+ // and dynamic tools. The large static tool definitions are loaded at runtime
+ // from the actions folder by generate_safe_outputs_tools.cjs.
+ toolsMetaJSON, err := generateToolsMetaJSON(workflowData, c.markdownPath)
if err != nil {
- mcpSetupGeneratorLog.Printf("Error generating filtered tools JSON: %v", err)
- // Fall back to empty array on error
- filteredToolsJSON = "[]"
+ mcpSetupGeneratorLog.Printf("Error generating tools meta JSON: %v", err)
+ // Fall back to empty meta on error
+ toolsMetaJSON = `{"description_suffixes":{},"repo_params":{},"dynamic_tools":[]}`
}
// Generate and write the validation configuration from Go source of truth
@@ -254,22 +260,27 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any,
yaml.WriteString(" - name: Write Safe Outputs Tools\n")
yaml.WriteString(" run: |\n")
- toolsDelimiter := GenerateHeredocDelimiter("SAFE_OUTPUTS_TOOLS")
- yaml.WriteString(" cat > /opt/gh-aw/safeoutputs/tools.json << '" + toolsDelimiter + "'\n")
- // Write each line of the indented JSON with proper YAML indentation
- for line := range strings.SplitSeq(filteredToolsJSON, "\n") {
+ toolsMetaDelimiter := GenerateHeredocDelimiter("SAFE_OUTPUTS_TOOLS_META")
+ yaml.WriteString(" cat > " + GhAwHome + "/safeoutputs/tools_meta.json << '" + toolsMetaDelimiter + "'\n")
+ // Write each line of the compact meta JSON with proper YAML indentation
+ for line := range strings.SplitSeq(toolsMetaJSON, "\n") {
yaml.WriteString(" " + line + "\n")
}
- yaml.WriteString(" " + toolsDelimiter + "\n")
+ yaml.WriteString(" " + toolsMetaDelimiter + "\n")
validationDelimiter := GenerateHeredocDelimiter("SAFE_OUTPUTS_VALIDATION")
- yaml.WriteString(" cat > /opt/gh-aw/safeoutputs/validation.json << '" + validationDelimiter + "'\n")
+ yaml.WriteString(" cat > " + GhAwHome + "/safeoutputs/validation.json << '" + validationDelimiter + "'\n")
// Write each line of the indented JSON with proper YAML indentation
for line := range strings.SplitSeq(validationConfigJSON, "\n") {
yaml.WriteString(" " + line + "\n")
}
yaml.WriteString(" " + validationDelimiter + "\n")
+ // Generate the final tools.json at runtime from the source file in the actions folder.
+ // generate_safe_outputs_tools.cjs reads safe_outputs_tools.json (deployed by actions/setup),
+ // applies the meta overrides from tools_meta.json, and writes tools.json.
+ yaml.WriteString(" node " + GhAwHome + "/actions/generate_safe_outputs_tools.cjs\n")
+
// Note: The MCP server entry point (mcp-server.cjs) is now copied by actions/setup
// from safe-outputs-mcp-server.cjs - no need to generate it here
@@ -302,8 +313,8 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any,
yaml.WriteString(" DEBUG: '*'\n")
yaml.WriteString(" GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}\n")
yaml.WriteString(" GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}\n")
- yaml.WriteString(" GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json\n")
- yaml.WriteString(" GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json\n")
+ yaml.WriteString(" GH_AW_SAFE_OUTPUTS_TOOLS_PATH: " + GhAwHomeExpr + "/safeoutputs/tools.json\n")
+ yaml.WriteString(" GH_AW_SAFE_OUTPUTS_CONFIG_PATH: " + GhAwHomeExpr + "/safeoutputs/config.json\n")
yaml.WriteString(" GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs\n")
yaml.WriteString(" run: |\n")
@@ -317,7 +328,7 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any,
yaml.WriteString(" \n")
// Call the bundled shell script to start the server
- yaml.WriteString(" bash /opt/gh-aw/actions/start_safe_outputs_server.sh\n")
+ yaml.WriteString(" bash " + GhAwHome + "/actions/start_safe_outputs_server.sh\n")
yaml.WriteString(" \n")
}
@@ -327,12 +338,12 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any,
// Step 1: Write config files (JavaScript files are now copied by actions/setup)
yaml.WriteString(" - name: Setup MCP Scripts Config\n")
yaml.WriteString(" run: |\n")
- yaml.WriteString(" mkdir -p /opt/gh-aw/mcp-scripts/logs\n")
+ yaml.WriteString(" mkdir -p " + GhAwHome + "/mcp-scripts/logs\n")
// Generate the tools.json configuration file
toolsJSON := generateMCPScriptsToolsConfig(workflowData.MCPScripts)
toolsDelimiter := GenerateHeredocDelimiter("MCP_SCRIPTS_TOOLS")
- yaml.WriteString(" cat > /opt/gh-aw/mcp-scripts/tools.json << '" + toolsDelimiter + "'\n")
+ yaml.WriteString(" cat > " + GhAwHome + "/mcp-scripts/tools.json << '" + toolsDelimiter + "'\n")
for line := range strings.SplitSeq(toolsJSON, "\n") {
yaml.WriteString(" " + line + "\n")
}
@@ -341,12 +352,12 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any,
// Generate the MCP server entry point
mcpScriptsMCPServer := generateMCPScriptsMCPServerScript(workflowData.MCPScripts)
serverDelimiter := GenerateHeredocDelimiter("MCP_SCRIPTS_SERVER")
- yaml.WriteString(" cat > /opt/gh-aw/mcp-scripts/mcp-server.cjs << '" + serverDelimiter + "'\n")
+ yaml.WriteString(" cat > " + GhAwHome + "/mcp-scripts/mcp-server.cjs << '" + serverDelimiter + "'\n")
for _, line := range FormatJavaScriptForYAML(mcpScriptsMCPServer) {
yaml.WriteString(line)
}
yaml.WriteString(" " + serverDelimiter + "\n")
- yaml.WriteString(" chmod +x /opt/gh-aw/mcp-scripts/mcp-server.cjs\n")
+ yaml.WriteString(" chmod +x " + GhAwHome + "/mcp-scripts/mcp-server.cjs\n")
yaml.WriteString(" \n")
// Step 2: Generate tool files (js/py/sh)
@@ -363,7 +374,7 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any,
// JavaScript tool
toolScript := generateMCPScriptJavaScriptToolScript(toolConfig)
jsDelimiter := GenerateHeredocDelimiter("MCP_SCRIPTS_JS_" + strings.ToUpper(toolName))
- fmt.Fprintf(yaml, " cat > /opt/gh-aw/mcp-scripts/%s.cjs << '%s'\n", toolName, jsDelimiter)
+ fmt.Fprintf(yaml, " cat > "+GhAwHome+"/mcp-scripts/%s.cjs << '%s'\n", toolName, jsDelimiter)
for _, line := range FormatJavaScriptForYAML(toolScript) {
yaml.WriteString(line)
}
@@ -372,27 +383,27 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any,
// Shell script tool
toolScript := generateMCPScriptShellToolScript(toolConfig)
shDelimiter := GenerateHeredocDelimiter("MCP_SCRIPTS_SH_" + strings.ToUpper(toolName))
- fmt.Fprintf(yaml, " cat > /opt/gh-aw/mcp-scripts/%s.sh << '%s'\n", toolName, shDelimiter)
+ fmt.Fprintf(yaml, " cat > "+GhAwHome+"/mcp-scripts/%s.sh << '%s'\n", toolName, shDelimiter)
for line := range strings.SplitSeq(toolScript, "\n") {
yaml.WriteString(" " + line + "\n")
}
fmt.Fprintf(yaml, " %s\n", shDelimiter)
- fmt.Fprintf(yaml, " chmod +x /opt/gh-aw/mcp-scripts/%s.sh\n", toolName)
+ fmt.Fprintf(yaml, " chmod +x "+GhAwHome+"/mcp-scripts/%s.sh\n", toolName)
} else if toolConfig.Py != "" {
// Python script tool
toolScript := generateMCPScriptPythonToolScript(toolConfig)
pyDelimiter := GenerateHeredocDelimiter("MCP_SCRIPTS_PY_" + strings.ToUpper(toolName))
- fmt.Fprintf(yaml, " cat > /opt/gh-aw/mcp-scripts/%s.py << '%s'\n", toolName, pyDelimiter)
+ fmt.Fprintf(yaml, " cat > "+GhAwHome+"/mcp-scripts/%s.py << '%s'\n", toolName, pyDelimiter)
for line := range strings.SplitSeq(toolScript, "\n") {
yaml.WriteString(" " + line + "\n")
}
fmt.Fprintf(yaml, " %s\n", pyDelimiter)
- fmt.Fprintf(yaml, " chmod +x /opt/gh-aw/mcp-scripts/%s.py\n", toolName)
+ fmt.Fprintf(yaml, " chmod +x "+GhAwHome+"/mcp-scripts/%s.py\n", toolName)
} else if toolConfig.Go != "" {
// Go script tool
toolScript := generateMCPScriptGoToolScript(toolConfig)
goDelimiter := GenerateHeredocDelimiter("MCP_SCRIPTS_GO_" + strings.ToUpper(toolName))
- fmt.Fprintf(yaml, " cat > /opt/gh-aw/mcp-scripts/%s.go << '%s'\n", toolName, goDelimiter)
+ fmt.Fprintf(yaml, " cat > "+GhAwHome+"/mcp-scripts/%s.go << '%s'\n", toolName, goDelimiter)
for line := range strings.SplitSeq(toolScript, "\n") {
yaml.WriteString(" " + line + "\n")
}
@@ -452,7 +463,7 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any,
yaml.WriteString(" \n")
// Call the bundled shell script to start the server
- yaml.WriteString(" bash /opt/gh-aw/actions/start_mcp_scripts_server.sh\n")
+ yaml.WriteString(" bash " + GhAwHome + "/actions/start_mcp_scripts_server.sh\n")
yaml.WriteString(" \n")
}
@@ -615,7 +626,9 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any,
if hasGitHub && getGitHubType(githubTool) == "remote" && engine.GetID() == "copilot" {
containerCmd.WriteString(" -e GITHUB_PERSONAL_ACCESS_TOKEN")
}
- containerCmd.WriteString(" -e GITHUB_MCP_LOCKDOWN")
+ // Automatic guard policy env vars (set from determine-automatic-lockdown step outputs)
+ containerCmd.WriteString(" -e GITHUB_MCP_GUARD_MIN_INTEGRITY")
+ containerCmd.WriteString(" -e GITHUB_MCP_GUARD_REPOS")
// Standard GitHub Actions environment variables (repository context)
containerCmd.WriteString(" -e GITHUB_REPOSITORY")
containerCmd.WriteString(" -e GITHUB_SERVER_URL")
@@ -678,7 +691,7 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any,
"MCP_GATEWAY_LOG_DIR", "GH_AW_MCP_LOG_DIR", "GH_AW_SAFE_OUTPUTS",
"GH_AW_SAFE_OUTPUTS_CONFIG_PATH", "GH_AW_SAFE_OUTPUTS_TOOLS_PATH",
"GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_ASSETS_ALLOWED_EXTS",
- "DEFAULT_BRANCH", "GITHUB_MCP_SERVER_TOKEN", "GITHUB_MCP_LOCKDOWN",
+ "DEFAULT_BRANCH", "GITHUB_MCP_SERVER_TOKEN", "GITHUB_MCP_GUARD_MIN_INTEGRITY", "GITHUB_MCP_GUARD_REPOS",
"GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "GITHUB_SHA", "GITHUB_WORKSPACE",
"GITHUB_TOKEN", "GITHUB_RUN_ID", "GITHUB_RUN_NUMBER", "GITHUB_RUN_ATTEMPT",
"GITHUB_JOB", "GITHUB_ACTION", "GITHUB_EVENT_NAME", "GITHUB_EVENT_PATH",
diff --git a/pkg/workflow/non_github_mcp_guard_policy_test.go b/pkg/workflow/non_github_mcp_guard_policy_test.go
index a395349245e..c81aa68b0c4 100644
--- a/pkg/workflow/non_github_mcp_guard_policy_test.go
+++ b/pkg/workflow/non_github_mcp_guard_policy_test.go
@@ -42,7 +42,7 @@ func TestDeriveWriteSinkGuardPolicyFromWorkflow(t *testing.T) {
description: "no github tool means no guard policy",
},
{
- name: "github tool without guard policy",
+ name: "github tool without guard policy (auto-lockdown)",
workflowData: &WorkflowData{
Tools: map[string]any{
"github": map[string]any{
@@ -50,8 +50,20 @@ func TestDeriveWriteSinkGuardPolicyFromWorkflow(t *testing.T) {
},
},
},
- expectNil: true,
- description: "github tool without repos/min-integrity has no guard policy",
+ expectNil: false,
+ expectedKey: "write-sink",
+ description: "github tool without repos/min-integrity triggers auto-lockdown which sets accept=[*]",
+ },
+ {
+ name: "github tool with nil value (auto-lockdown)",
+ workflowData: &WorkflowData{
+ Tools: map[string]any{
+ "github": nil,
+ },
+ },
+ expectNil: false,
+ expectedKey: "write-sink",
+ description: "github tool with nil value triggers auto-lockdown which sets accept=[*]",
},
{
name: "github tool with repos=all",
@@ -366,9 +378,10 @@ func TestAllNonGitHubMCPServersGetGuardPoliciesViaRenderer(t *testing.T) {
})
}
-// TestNonGitHubMCPServersNoGuardPoliciesWhenGitHubNotConfigured verifies that servers
-// do not get guard policies when the GitHub tool has no guard policy configured
-func TestNonGitHubMCPServersNoGuardPoliciesWhenGitHubNotConfigured(t *testing.T) {
+// TestNonGitHubMCPServersGetGuardPoliciesFromAutoLockdown verifies that non-GitHub MCP servers
+// get write-sink: {accept: ["*"]} guard policies when the GitHub tool is configured without
+// explicit guard policies (auto-lockdown detection will set repos=all at runtime)
+func TestNonGitHubMCPServersGetGuardPoliciesFromAutoLockdown(t *testing.T) {
workflowData := &WorkflowData{
Tools: map[string]any{
"github": map[string]any{
@@ -379,9 +392,16 @@ func TestNonGitHubMCPServersNoGuardPoliciesWhenGitHubNotConfigured(t *testing.T)
}
policies := deriveWriteSinkGuardPolicyFromWorkflow(workflowData)
- assert.Nil(t, policies, "no guard policies when GitHub has no guard policy configured")
+ require.NotNil(t, policies, "guard policies should be derived when GitHub tool triggers auto-lockdown")
- // Verify playwright JSON rendering has no guard-policies
+ expectedPolicies := map[string]any{
+ "write-sink": map[string]any{
+ "accept": []string{"*"},
+ },
+ }
+ assert.Equal(t, expectedPolicies, policies, "auto-lockdown should produce write-sink with accept=*")
+
+ // Verify playwright JSON rendering has guard-policies
var output strings.Builder
renderer := NewMCPConfigRenderer(MCPRendererOptions{
Format: "json",
@@ -389,7 +409,167 @@ func TestNonGitHubMCPServersNoGuardPoliciesWhenGitHubNotConfigured(t *testing.T)
WriteSinkGuardPolicies: policies,
})
renderer.RenderPlaywrightMCP(&output, nil)
- assert.NotContains(t, output.String(), "guard-policies", "playwright should not have guard-policies when GitHub has no guard policy")
+ assert.Contains(t, output.String(), "guard-policies", "playwright should have guard-policies when auto-lockdown is active")
+}
+
+// TestNonGitHubMCPServersNoGuardPoliciesWithGitHubApp verifies that non-GitHub MCP servers
+// do NOT get write-sink guard policies when a GitHub App is configured.
+// GitHub App tokens are already repo-scoped, so auto-lockdown detection is skipped.
+func TestNonGitHubMCPServersNoGuardPoliciesWithGitHubApp(t *testing.T) {
+ workflowData := &WorkflowData{
+ Tools: map[string]any{
+ "github": map[string]any{
+ "toolsets": []string{"default"},
+ "github-app": map[string]any{
+ "app-id": "12345",
+ },
+ },
+ "playwright": nil,
+ },
+ }
+
+ policies := deriveWriteSinkGuardPolicyFromWorkflow(workflowData)
+ assert.Nil(t, policies, "no guard policies when GitHub App is configured (auto-lockdown is skipped)")
+}
+
+// TestAllNonGitHubMCPServersGetWriteSinkWhenGitHubHasAllowOnly verifies that when the GitHub
+// MCP server has an explicit allow-only guard-policy configured (repos + min-integrity),
+// ALL non-GitHub MCP server types receive a corresponding write-sink guard-policy via
+// the MCPConfigRendererUnified.
+func TestAllNonGitHubMCPServersGetWriteSinkWhenGitHubHasAllowOnly(t *testing.T) {
+ tests := []struct {
+ name string
+ githubConfig map[string]any
+ expectedAccept []string
+ description string
+ }{
+ {
+ name: "repos=all min-integrity=none",
+ githubConfig: map[string]any{
+ "repos": "all",
+ "min-integrity": "none",
+ },
+ expectedAccept: []string{"*"},
+ description: "repos=all should produce accept=[*]",
+ },
+ {
+ name: "repos=public min-integrity=approved",
+ githubConfig: map[string]any{
+ "repos": "public",
+ "min-integrity": "approved",
+ },
+ expectedAccept: []string{"*"},
+ description: "repos=public should produce accept=[*]",
+ },
+ {
+ name: "repos=specific-repo min-integrity=approved",
+ githubConfig: map[string]any{
+ "repos": "myorg/myrepo",
+ "min-integrity": "approved",
+ },
+ expectedAccept: []string{"private:myorg/myrepo"},
+ description: "specific repo should produce accept=[private:myorg/myrepo]",
+ },
+ {
+ name: "repos=owner-wildcard min-integrity=merged",
+ githubConfig: map[string]any{
+ "repos": "myorg/*",
+ "min-integrity": "merged",
+ },
+ expectedAccept: []string{"private:myorg"},
+ description: "owner/* should produce accept=[private:myorg]",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ workflowData := &WorkflowData{
+ Tools: map[string]any{
+ "github": tt.githubConfig,
+ "playwright": nil,
+ "serena": nil,
+ "agentic-workflows": nil,
+ "web-fetch": nil,
+ },
+ }
+
+ // Derive write-sink guard policies from the configured allow-only GitHub guard policy
+ policies := deriveWriteSinkGuardPolicyFromWorkflow(workflowData)
+ require.NotNil(t, policies, "write-sink guard policies should be derived when GitHub has allow-only policy: %s", tt.description)
+
+ writeSink, ok := policies["write-sink"].(map[string]any)
+ require.True(t, ok, "write-sink should be a map: %s", tt.description)
+ assert.Equal(t, tt.expectedAccept, writeSink["accept"], "accept list should match: %s", tt.description)
+
+ // Verify every non-GitHub MCP server type gets the guard policies via the renderer
+ serverChecks := []struct {
+ serverName string
+ render func(*strings.Builder, *MCPConfigRendererUnified)
+ }{
+ {
+ serverName: "playwright",
+ render: func(out *strings.Builder, r *MCPConfigRendererUnified) {
+ r.RenderPlaywrightMCP(out, nil)
+ },
+ },
+ {
+ serverName: "serena",
+ render: func(out *strings.Builder, r *MCPConfigRendererUnified) {
+ r.RenderSerenaMCP(out, nil)
+ },
+ },
+ {
+ serverName: "agentic-workflows",
+ render: func(out *strings.Builder, r *MCPConfigRendererUnified) {
+ r.RenderAgenticWorkflowsMCP(out)
+ },
+ },
+ {
+ serverName: "mcp-scripts",
+ render: func(out *strings.Builder, r *MCPConfigRendererUnified) {
+ mcpScripts := &MCPScriptsConfig{}
+ r.RenderMCPScriptsMCP(out, mcpScripts, workflowData)
+ },
+ },
+ {
+ serverName: "safe-outputs",
+ render: func(out *strings.Builder, r *MCPConfigRendererUnified) {
+ r.RenderSafeOutputsMCP(out, workflowData)
+ },
+ },
+ }
+
+ for _, check := range serverChecks {
+ t.Run(check.serverName+" JSON", func(t *testing.T) {
+ renderer := NewMCPConfigRenderer(MCPRendererOptions{
+ Format: "json",
+ IsLast: true,
+ WriteSinkGuardPolicies: policies,
+ })
+ var output strings.Builder
+ check.render(&output, renderer)
+ result := output.String()
+ assert.Contains(t, result, "\"guard-policies\"",
+ "%s should have guard-policies when GitHub has allow-only policy: %s", check.serverName, tt.description)
+ assert.Contains(t, result, "\"write-sink\"",
+ "%s should have write-sink policy: %s", check.serverName, tt.description)
+ assert.Contains(t, result, "\"accept\"",
+ "%s should have accept field: %s", check.serverName, tt.description)
+ })
+ }
+
+ // Also test web-fetch (has its own render function)
+ t.Run("web-fetch JSON", func(t *testing.T) {
+ var output strings.Builder
+ renderMCPFetchServerConfig(&output, "json", " ", true, false, policies)
+ result := output.String()
+ assert.Contains(t, result, "\"guard-policies\"",
+ "web-fetch should have guard-policies when GitHub has allow-only policy: %s", tt.description)
+ assert.Contains(t, result, "\"write-sink\"",
+ "web-fetch should have write-sink policy: %s", tt.description)
+ })
+ })
+ }
}
// TestNonGitHubMCPServersGetGuardPoliciesWhenGitHubConfigured verifies the end-to-end flow:
diff --git a/pkg/workflow/notify_comment.go b/pkg/workflow/notify_comment.go
index d6cd6e9dcf4..ca494e251b4 100644
--- a/pkg/workflow/notify_comment.go
+++ b/pkg/workflow/notify_comment.go
@@ -119,7 +119,7 @@ func (c *Compiler) buildConclusionJob(data *WorkflowData, mainJobName string, sa
StepID: "missing_tool",
MainJobName: mainJobName,
CustomEnvVars: missingToolEnvVars,
- Script: "const { main } = require('/opt/gh-aw/actions/missing_tool.cjs'); await main();",
+ Script: "const { main } = require(" + JsRequireGhAw("actions/missing_tool.cjs") + "); await main();",
ScriptFile: "missing_tool.cjs",
CustomToken: data.SafeOutputs.MissingTool.GitHubToken,
})
@@ -230,7 +230,7 @@ func (c *Compiler) buildConclusionJob(data *WorkflowData, mainJobName string, sa
StepID: "handle_agent_failure",
MainJobName: mainJobName,
CustomEnvVars: agentFailureEnvVars,
- Script: "const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs'); await main();",
+ Script: "const { main } = require(" + JsRequireGhAw("actions/handle_agent_failure.cjs") + "); await main();",
ScriptFile: "handle_agent_failure.cjs",
CustomToken: "", // Will use default GITHUB_TOKEN
})
@@ -260,7 +260,7 @@ func (c *Compiler) buildConclusionJob(data *WorkflowData, mainJobName string, sa
StepID: "handle_noop_message",
MainJobName: mainJobName,
CustomEnvVars: noopMessageEnvVars,
- Script: "const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); await main();",
+ Script: "const { main } = require(" + JsRequireGhAw("actions/handle_noop_message.cjs") + "); await main();",
ScriptFile: "handle_noop_message.cjs",
CustomToken: "", // Will use default GITHUB_TOKEN
})
@@ -281,7 +281,7 @@ func (c *Compiler) buildConclusionJob(data *WorkflowData, mainJobName string, sa
StepID: "handle_create_pr_error",
MainJobName: mainJobName,
CustomEnvVars: createPRErrorEnvVars,
- Script: "const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs'); await main();",
+ Script: "const { main } = require(" + JsRequireGhAw("actions/handle_create_pr_error.cjs") + "); await main();",
ScriptFile: "handle_create_pr_error.cjs",
CustomToken: "", // Will use default GITHUB_TOKEN
})
@@ -431,6 +431,7 @@ func (c *Compiler) buildConclusionJob(data *WorkflowData, mainJobName string, sa
Environment: c.indentYAMLLines(resolveSafeOutputsEnvironment(data), " "),
Permissions: permissions.RenderToYAML(),
Concurrency: concurrency,
+ Env: map[string]string{"GH_AW_HOME": GhAwHomeExprDefault},
Steps: steps,
Needs: needs,
Outputs: outputs,
diff --git a/pkg/workflow/npm_validation.go b/pkg/workflow/npm_validation.go
index c5559842f15..20cd5030d7c 100644
--- a/pkg/workflow/npm_validation.go
+++ b/pkg/workflow/npm_validation.go
@@ -17,7 +17,7 @@
// NPM package validation queries the npm registry using the npm CLI:
// - Uses `npm view name` to check package existence
// - Returns hard errors if packages don't exist (unlike pip validation)
-// - Requires npm to be installed on the system
+// - Returns ErrNpmNotAvailable (treated as a warning) when npm is not installed
//
// # When to Add Validation Here
//
@@ -34,6 +34,7 @@
package workflow
import (
+ "errors"
"fmt"
"os"
"os/exec"
@@ -44,6 +45,16 @@ import (
var npmValidationLog = newValidationLogger("npm")
+// ErrNpmNotAvailable is returned by validateNpxPackages when npm is not installed on the system.
+// Callers should treat this as a warning rather than a hard error, since the workflow may still
+// compile and run successfully in environments that have npm (e.g., GitHub Actions).
+var ErrNpmNotAvailable = errors.New("npm not available")
+
+// isErrNpmNotAvailable reports whether err indicates that npm is not installed on the system.
+func isErrNpmNotAvailable(err error) bool {
+ return errors.Is(err, ErrNpmNotAvailable)
+}
+
// validateNpxPackages validates that npx packages are available on npm registry
func (c *Compiler) validateNpxPackages(workflowData *WorkflowData) error {
packages := extractNpxPackages(workflowData)
@@ -58,13 +69,7 @@ func (c *Compiler) validateNpxPackages(workflowData *WorkflowData) error {
_, err := exec.LookPath("npm")
if err != nil {
npmValidationLog.Print("npm command not found, cannot validate npx packages")
- return NewOperationError(
- "validate",
- "npx packages",
- "",
- err,
- "Install Node.js and npm to enable npx package validation:\n\nUsing nvm (recommended):\n$ nvm install --lts\n\nOr download from https://nodejs.org\n\nAlternatively, disable validation by setting GH_AW_SKIP_NPX_VALIDATION=true",
- )
+ return ErrNpmNotAvailable
}
var errors []string
diff --git a/pkg/workflow/npm_validation_wasm.go b/pkg/workflow/npm_validation_wasm.go
index 4eb6d953935..3ee567f6e92 100644
--- a/pkg/workflow/npm_validation_wasm.go
+++ b/pkg/workflow/npm_validation_wasm.go
@@ -2,6 +2,16 @@
package workflow
+import "errors"
+
+// ErrNpmNotAvailable is returned by validateNpxPackages when npm is not installed on the system.
+var ErrNpmNotAvailable = errors.New("npm not available")
+
+// isErrNpmNotAvailable reports whether err indicates that npm is not installed on the system.
+func isErrNpmNotAvailable(err error) bool {
+ return errors.Is(err, ErrNpmNotAvailable)
+}
+
func (c *Compiler) validateNpxPackages(workflowData *WorkflowData) error {
return nil
}
diff --git a/pkg/workflow/on_steps_test.go b/pkg/workflow/on_steps_test.go
new file mode 100644
index 00000000000..ea00e75d0c3
--- /dev/null
+++ b/pkg/workflow/on_steps_test.go
@@ -0,0 +1,509 @@
+//go:build !integration
+
+package workflow
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/github/gh-aw/pkg/stringutil"
+ "github.com/github/gh-aw/pkg/testutil"
+)
+
+// TestOnSteps tests that on.steps are injected into the pre-activation job and their
+// results are wired as pre-activation outputs.
+func TestOnSteps(t *testing.T) {
+ tmpDir := testutil.TempDir(t, "on-steps-test")
+ compiler := NewCompiler()
+
+ t.Run("on_steps_creates_pre_activation_job", func(t *testing.T) {
+ workflowContent := `---
+on:
+ workflow_dispatch: null
+ roles: all
+ steps:
+ - name: Gate check
+ id: gate
+ run: echo "checking..."
+engine: copilot
+---
+
+Test workflow with on.steps creating pre-activation job
+`
+ workflowFile := filepath.Join(tmpDir, "test-on-steps-only.md")
+ if err := os.WriteFile(workflowFile, []byte(workflowContent), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ err := compiler.CompileWorkflow(workflowFile)
+ if err != nil {
+ t.Fatalf("CompileWorkflow() returned error: %v", err)
+ }
+
+ lockFile := stringutil.MarkdownToLockFile(workflowFile)
+ lockContent, err := os.ReadFile(lockFile)
+ if err != nil {
+ t.Fatalf("Failed to read lock file: %v", err)
+ }
+ lockContentStr := string(lockContent)
+
+ // Verify pre_activation job is created
+ if !strings.Contains(lockContentStr, "pre_activation:") {
+ t.Error("Expected pre_activation job to be created when on.steps is used")
+ }
+
+ // Verify the step is included
+ if !strings.Contains(lockContentStr, "Gate check") {
+ t.Error("Expected Gate check step to be in pre_activation job")
+ }
+ if !strings.Contains(lockContentStr, "id: gate") {
+ t.Error("Expected gate step ID to be in pre_activation job")
+ }
+
+ // Verify the output is wired
+ if !strings.Contains(lockContentStr, "gate_result: ${{ steps.gate.outcome }}") {
+ t.Errorf("Expected gate_result output to be wired. Lock file:\n%s", lockContentStr)
+ }
+
+ // Verify activated output is 'true' when on.steps is the only condition
+ if !strings.Contains(lockContentStr, "activated: ${{ 'true' }}") {
+ t.Errorf("Expected activated output to be 'true'. Lock file:\n%s", lockContentStr)
+ }
+ })
+
+ t.Run("on_steps_with_other_checks", func(t *testing.T) {
+ workflowContent := `---
+on:
+ issues:
+ types: [opened]
+ roles: [admin, maintainer]
+ steps:
+ - name: Gate check
+ id: gate
+ run: echo "checking..."
+engine: copilot
+---
+
+Test workflow with on.steps combined with role checks
+`
+ workflowFile := filepath.Join(tmpDir, "test-on-steps-with-roles.md")
+ if err := os.WriteFile(workflowFile, []byte(workflowContent), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ err := compiler.CompileWorkflow(workflowFile)
+ if err != nil {
+ t.Fatalf("CompileWorkflow() returned error: %v", err)
+ }
+
+ lockFile := stringutil.MarkdownToLockFile(workflowFile)
+ lockContent, err := os.ReadFile(lockFile)
+ if err != nil {
+ t.Fatalf("Failed to read lock file: %v", err)
+ }
+ lockContentStr := string(lockContent)
+
+ // The on.steps step should be present
+ if !strings.Contains(lockContentStr, "Gate check") {
+ t.Error("Expected Gate check step to be in pre_activation job")
+ }
+
+ // The gate_result output should be wired
+ if !strings.Contains(lockContentStr, "gate_result: ${{ steps.gate.outcome }}") {
+ t.Errorf("Expected gate_result output. Lock file:\n%s", lockContentStr)
+ }
+
+ // The activated output should use the membership check (not 'true')
+ if strings.Contains(lockContentStr, "activated: ${{ 'true' }}") {
+ t.Error("Expected activated output to use membership check, not 'true'")
+ }
+ if !strings.Contains(lockContentStr, "check_membership") {
+ t.Error("Expected membership check step to be present")
+ }
+ })
+
+ t.Run("on_steps_multiple_steps_with_ids", func(t *testing.T) {
+ workflowContent := `---
+on:
+ workflow_dispatch: null
+ roles: all
+ steps:
+ - name: First check
+ id: first
+ run: echo "first..."
+ - name: Second check
+ id: second
+ run: echo "second..."
+ - name: No ID step
+ run: echo "no id"
+engine: copilot
+---
+
+Test workflow with multiple on.steps
+`
+ workflowFile := filepath.Join(tmpDir, "test-on-steps-multi.md")
+ if err := os.WriteFile(workflowFile, []byte(workflowContent), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ err := compiler.CompileWorkflow(workflowFile)
+ if err != nil {
+ t.Fatalf("CompileWorkflow() returned error: %v", err)
+ }
+
+ lockFile := stringutil.MarkdownToLockFile(workflowFile)
+ lockContent, err := os.ReadFile(lockFile)
+ if err != nil {
+ t.Fatalf("Failed to read lock file: %v", err)
+ }
+ lockContentStr := string(lockContent)
+
+ // Both step IDs should have outputs
+ if !strings.Contains(lockContentStr, "first_result: ${{ steps.first.outcome }}") {
+ t.Errorf("Expected first_result output. Lock file:\n%s", lockContentStr)
+ }
+ if !strings.Contains(lockContentStr, "second_result: ${{ steps.second.outcome }}") {
+ t.Errorf("Expected second_result output. Lock file:\n%s", lockContentStr)
+ }
+
+ // All three steps should be present
+ if !strings.Contains(lockContentStr, "First check") {
+ t.Error("Expected First check step to be in pre_activation job")
+ }
+ if !strings.Contains(lockContentStr, "Second check") {
+ t.Error("Expected Second check step to be in pre_activation job")
+ }
+ if !strings.Contains(lockContentStr, "No ID step") {
+ t.Error("Expected No ID step to be in pre_activation job")
+ }
+ })
+
+ t.Run("on_steps_step_appended_after_builtin_checks", func(t *testing.T) {
+ workflowContent := `---
+on:
+ issues:
+ types: [opened]
+ roles: [admin, maintainer]
+ steps:
+ - name: Gate check
+ id: gate
+ run: echo "checking..."
+engine: copilot
+---
+
+Test on.steps are appended after built-in checks
+`
+ workflowFile := filepath.Join(tmpDir, "test-on-steps-order.md")
+ if err := os.WriteFile(workflowFile, []byte(workflowContent), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ err := compiler.CompileWorkflow(workflowFile)
+ if err != nil {
+ t.Fatalf("CompileWorkflow() returned error: %v", err)
+ }
+
+ lockFile := stringutil.MarkdownToLockFile(workflowFile)
+ lockContent, err := os.ReadFile(lockFile)
+ if err != nil {
+ t.Fatalf("Failed to read lock file: %v", err)
+ }
+ lockContentStr := string(lockContent)
+
+ // The membership check step should appear before the gate step in the pre_activation steps.
+ // Search for step-level indented IDs (8 spaces = within steps array under pre_activation job)
+ membershipStepIdx := strings.Index(lockContentStr, " id: check_membership")
+ gateStepIdx := strings.Index(lockContentStr, " id: gate")
+ if membershipStepIdx == -1 || gateStepIdx == -1 {
+ t.Fatalf("Expected both check_membership step and gate step to be present. Lock file:\n%s", lockContentStr)
+ }
+ if membershipStepIdx > gateStepIdx {
+ t.Error("Expected membership check step to appear before on.steps gate step in pre_activation job")
+ }
+ })
+}
+
+// TestExtractOnSteps tests the extractOnSteps function directly
+func TestExtractOnSteps(t *testing.T) {
+ tests := []struct {
+ name string
+ frontmatter map[string]any
+ expectSteps int
+ expectError bool
+ errorContains string
+ }{
+ {
+ name: "no_on_section",
+ frontmatter: map[string]any{},
+ expectSteps: 0,
+ expectError: false,
+ },
+ {
+ name: "on_section_string",
+ frontmatter: map[string]any{
+ "on": "push",
+ },
+ expectSteps: 0,
+ expectError: false,
+ },
+ {
+ name: "on_section_without_steps",
+ frontmatter: map[string]any{
+ "on": map[string]any{
+ "push": nil,
+ },
+ },
+ expectSteps: 0,
+ expectError: false,
+ },
+ {
+ name: "on_steps_with_steps",
+ frontmatter: map[string]any{
+ "on": map[string]any{
+ "issues": nil,
+ "steps": []any{
+ map[string]any{"name": "Step 1", "id": "step1", "run": "echo ok"},
+ map[string]any{"name": "Step 2", "run": "echo ok2"},
+ },
+ },
+ },
+ expectSteps: 2,
+ expectError: false,
+ },
+ {
+ name: "on_steps_invalid_type",
+ frontmatter: map[string]any{
+ "on": map[string]any{
+ "steps": "not an array",
+ },
+ },
+ expectError: true,
+ errorContains: "on.steps must be an array",
+ },
+ {
+ name: "on_steps_invalid_step_type",
+ frontmatter: map[string]any{
+ "on": map[string]any{
+ "steps": []any{"not a map"},
+ },
+ },
+ expectError: true,
+ errorContains: "on.steps[0] must be an object",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ steps, err := extractOnSteps(tt.frontmatter)
+
+ if tt.expectError {
+ if err == nil {
+ t.Fatalf("Expected error containing '%s', but got none", tt.errorContains)
+ }
+ if !strings.Contains(err.Error(), tt.errorContains) {
+ t.Errorf("Expected error containing '%s', got: %v", tt.errorContains, err)
+ }
+ return
+ }
+
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+
+ if len(steps) != tt.expectSteps {
+ t.Errorf("Expected %d steps, got %d", tt.expectSteps, len(steps))
+ }
+ })
+ }
+}
+
+// TestExtractOnPermissions tests the extractOnPermissions function directly
+func TestExtractOnPermissions(t *testing.T) {
+ tests := []struct {
+ name string
+ frontmatter map[string]any
+ expectNil bool
+ expectScopes map[string]string // scope -> level
+ }{
+ {
+ name: "no_on_section",
+ frontmatter: map[string]any{},
+ expectNil: true,
+ },
+ {
+ name: "on_section_string",
+ frontmatter: map[string]any{
+ "on": "push",
+ },
+ expectNil: true,
+ },
+ {
+ name: "on_section_without_permissions",
+ frontmatter: map[string]any{
+ "on": map[string]any{
+ "push": nil,
+ },
+ },
+ expectNil: true,
+ },
+ {
+ name: "on_permissions_issues_read",
+ frontmatter: map[string]any{
+ "on": map[string]any{
+ "issues": nil,
+ "permissions": map[string]any{
+ "issues": "read",
+ },
+ },
+ },
+ expectNil: false,
+ expectScopes: map[string]string{"issues": "read"},
+ },
+ {
+ name: "on_permissions_multiple_scopes",
+ frontmatter: map[string]any{
+ "on": map[string]any{
+ "issues": nil,
+ "permissions": map[string]any{
+ "issues": "read",
+ "pull-requests": "read",
+ },
+ },
+ },
+ expectNil: false,
+ expectScopes: map[string]string{"issues": "read", "pull-requests": "read"},
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ perms := extractOnPermissions(tt.frontmatter)
+ if tt.expectNil {
+ if perms != nil {
+ t.Errorf("Expected nil permissions, got non-nil")
+ }
+ return
+ }
+ if perms == nil {
+ t.Fatalf("Expected non-nil permissions, got nil")
+ }
+ for scope, wantLevel := range tt.expectScopes {
+ gotLevel, ok := perms.Get(convertStringToPermissionScope(scope))
+ if !ok {
+ t.Errorf("Expected scope %s to be set", scope)
+ continue
+ }
+ if string(gotLevel) != wantLevel {
+ t.Errorf("Expected scope %s = %s, got %s", scope, wantLevel, gotLevel)
+ }
+ }
+ })
+ }
+}
+
+// TestOnPermissionsAppliedToPreActivation tests that on.permissions are applied to the pre-activation job
+func TestOnPermissionsAppliedToPreActivation(t *testing.T) {
+ tmpDir := testutil.TempDir(t, "on-permissions-test")
+ compiler := NewCompiler()
+
+ workflowContent := `---
+on:
+ workflow_dispatch: null
+ roles: [admin]
+ permissions:
+ issues: read
+ pull-requests: read
+ steps:
+ - name: Check something
+ id: check
+ uses: actions/github-script@v8
+ with:
+ script: |
+ const issues = await github.rest.issues.listForRepo({ owner: context.repo.owner, repo: context.repo.repo });
+ core.setOutput('count', issues.data.length);
+engine: copilot
+---
+
+Workflow with on.permissions
+`
+ workflowFile := filepath.Join(tmpDir, "test-on-permissions.md")
+ if err := os.WriteFile(workflowFile, []byte(workflowContent), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ err := compiler.CompileWorkflow(workflowFile)
+ if err != nil {
+ t.Fatalf("CompileWorkflow() returned error: %v", err)
+ }
+
+ lockFile := stringutil.MarkdownToLockFile(workflowFile)
+ lockContent, err := os.ReadFile(lockFile)
+ if err != nil {
+ t.Fatalf("Failed to read lock file: %v", err)
+ }
+ lockContentStr := string(lockContent)
+
+ // The pre_activation job should have issues: read and pull-requests: read permissions
+ if !strings.Contains(lockContentStr, "issues: read") {
+ t.Error("Expected issues: read permission in pre_activation job")
+ }
+ if !strings.Contains(lockContentStr, "pull-requests: read") {
+ t.Error("Expected pull-requests: read permission in pre_activation job")
+ }
+
+ // The on.permissions should be commented out in the compiled on: section
+ if !strings.Contains(lockContentStr, "# permissions:") {
+ t.Error("Expected on.permissions to be commented out in the on: section")
+ }
+}
+
+// TestReferencesPreActivationOutputs tests the referencesPreActivationOutputs function
+func TestReferencesPreActivationOutputs(t *testing.T) {
+ tests := []struct {
+ name string
+ condition string
+ expected bool
+ }{
+ {
+ name: "empty_condition",
+ condition: "",
+ expected: false,
+ },
+ {
+ name: "references_pre_activation_outputs",
+ condition: "needs.pre_activation.outputs.has_issues == 'true'",
+ expected: true,
+ },
+ {
+ name: "references_custom_job_outputs",
+ condition: "needs.search_issues.outputs.has_issues == 'true'",
+ expected: false,
+ },
+ {
+ name: "references_pre_activation_activated",
+ condition: "needs.pre_activation.outputs.activated == 'true'",
+ expected: true,
+ },
+ {
+ name: "references_pre_activation_result_not_outputs",
+ condition: "needs.pre_activation.result == 'success'",
+ expected: false,
+ },
+ {
+ name: "references_pre_activation_arbitrary_output",
+ condition: "needs.pre_activation.outputs.custom_gate_check == 'passed'",
+ expected: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := referencesPreActivationOutputs(tt.condition)
+ if result != tt.expected {
+ t.Errorf("referencesPreActivationOutputs(%q) = %v, want %v", tt.condition, result, tt.expected)
+ }
+ })
+ }
+}
diff --git a/pkg/workflow/pr.go b/pkg/workflow/pr.go
index 32d1f63bc07..0802b70c2d7 100644
--- a/pkg/workflow/pr.go
+++ b/pkg/workflow/pr.go
@@ -66,16 +66,16 @@ func (c *Compiler) generatePRReadyForReviewCheckout(yaml *strings.Builder, data
if useRequire {
// Use require() to load script from copied files using setup_globals helper
- yaml.WriteString(" const { setupGlobals } = require('" + SetupActionDestination + "/setup_globals.cjs');\n")
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
- yaml.WriteString(" const { main } = require('" + SetupActionDestination + "/checkout_pr_branch.cjs');\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/checkout_pr_branch.cjs") + ");\n")
yaml.WriteString(" await main();\n")
} else {
// Inline JavaScript: Attach GitHub Actions builtin objects to global scope before script execution
- yaml.WriteString(" const { setupGlobals } = require('" + SetupActionDestination + "/setup_globals.cjs');\n")
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
// Add the JavaScript for checking out the PR branch
- WriteJavaScriptToYAML(yaml, "const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); await main();")
+ WriteJavaScriptToYAML(yaml, "const { main } = require("+JsRequireGhAw("actions/checkout_pr_branch.cjs")+"); await main();")
}
}
diff --git a/pkg/workflow/pr_checkout_test.go b/pkg/workflow/pr_checkout_test.go
index 22ca2c6d498..497f8ac7f71 100644
--- a/pkg/workflow/pr_checkout_test.go
+++ b/pkg/workflow/pr_checkout_test.go
@@ -207,7 +207,7 @@ Test workflow with permissions but checkout should be conditional.
}
// Check for PR context prompt using cat command
- hasPRPrompt := strings.Contains(lockStr, "cat \"/opt/gh-aw/prompts/pr_context_prompt.md\"")
+ hasPRPrompt := strings.Contains(lockStr, "cat \"${GH_AW_HOME}/prompts/pr_context_prompt.md\"")
if hasPRPrompt != tt.expectPRPrompt {
t.Errorf("Expected PR context prompt: %v, got: %v", tt.expectPRPrompt, hasPRPrompt)
}
@@ -228,7 +228,7 @@ Test workflow with permissions but checkout should be conditional.
// If PR prompt is expected, verify the cat command references the correct file
if tt.expectPRPrompt {
- if !strings.Contains(lockStr, "cat \"/opt/gh-aw/prompts/pr_context_prompt.md\"") {
+ if !strings.Contains(lockStr, "cat \"${GH_AW_HOME}/prompts/pr_context_prompt.md\"") {
t.Error("PR context prompt should reference pr_context_prompt.md file")
}
}
diff --git a/pkg/workflow/prompt_constants.go b/pkg/workflow/prompt_constants.go
index 2f9d9c39be4..e5c08c889b7 100644
--- a/pkg/workflow/prompt_constants.go
+++ b/pkg/workflow/prompt_constants.go
@@ -4,20 +4,23 @@ import _ "embed"
// Prompt file paths at runtime (copied by setup action to /opt/gh-aw/prompts)
const (
- promptsDir = "/opt/gh-aw/prompts"
- prContextPromptFile = "pr_context_prompt.md"
- tempFolderPromptFile = "temp_folder_prompt.md"
- playwrightPromptFile = "playwright_prompt.md"
- markdownPromptFile = "markdown.md"
- xpiaPromptFile = "xpia.md"
- cacheMemoryPromptFile = "cache_memory_prompt.md"
- cacheMemoryPromptMultiFile = "cache_memory_prompt_multi.md"
- repoMemoryPromptFile = "repo_memory_prompt.md"
- repoMemoryPromptMultiFile = "repo_memory_prompt_multi.md"
- safeOutputsPromptFile = "safe_outputs_prompt.md"
- safeOutputsCreatePRFile = "safe_outputs_create_pull_request.md"
- safeOutputsPushToBranchFile = "safe_outputs_push_to_pr_branch.md"
- safeOutputsAutoCreateIssueFile = "safe_outputs_auto_create_issue.md"
+ promptsDir = GhAwHome + "/prompts"
+ prContextPromptFile = "pr_context_prompt.md"
+ tempFolderPromptFile = "temp_folder_prompt.md"
+ playwrightPromptFile = "playwright_prompt.md"
+ markdownPromptFile = "markdown.md"
+ xpiaPromptFile = "xpia.md"
+ cacheMemoryPromptFile = "cache_memory_prompt.md"
+ cacheMemoryPromptMultiFile = "cache_memory_prompt_multi.md"
+ repoMemoryPromptFile = "repo_memory_prompt.md"
+ repoMemoryPromptMultiFile = "repo_memory_prompt_multi.md"
+ safeOutputsPromptFile = "safe_outputs_prompt.md"
+ safeOutputsCreatePRFile = "safe_outputs_create_pull_request.md"
+ safeOutputsPushToBranchFile = "safe_outputs_push_to_pr_branch.md"
+ safeOutputsAutoCreateIssueFile = "safe_outputs_auto_create_issue.md"
+ agenticWorkflowsGuideFile = "agentic_workflows_guide.md"
+ githubMCPToolsPromptFile = "github_mcp_tools_prompt.md"
+ githubMCPToolsWithSafeOutputsPromptFile = "github_mcp_tools_with_safeoutputs_prompt.md"
)
// GitHub context prompt is kept embedded because it contains GitHub Actions expressions
diff --git a/pkg/workflow/prompts.go b/pkg/workflow/prompts.go
index 82e3732f7d0..00b9e7b132c 100644
--- a/pkg/workflow/prompts.go
+++ b/pkg/workflow/prompts.go
@@ -29,6 +29,21 @@ func hasPlaywrightTool(parsedTools *Tools) bool {
return hasPlaywright
}
+// ============================================================================
+// Tool Prompts - Agentic Workflows
+// ============================================================================
+
+// hasAgenticWorkflowsTool checks if the agentic workflows tool is enabled in the tools configuration
+func hasAgenticWorkflowsTool(parsedTools *Tools) bool {
+ if parsedTools == nil {
+ log.Print("Checking for agentic-workflows tool: no parsed tools provided")
+ return false
+ }
+ hasAgenticWorkflows := parsedTools.AgenticWorkflows != nil
+ log.Printf("Agentic-workflows tool enabled: %v", hasAgenticWorkflows)
+ return hasAgenticWorkflows
+}
+
// ============================================================================
// PR Context Prompts
// ============================================================================
diff --git a/pkg/workflow/prompts_test.go b/pkg/workflow/prompts_test.go
index f560c671d4f..f71913a8898 100644
--- a/pkg/workflow/prompts_test.go
+++ b/pkg/workflow/prompts_test.go
@@ -227,8 +227,8 @@ This is a test workflow with cache-memory enabled.
}
// Test 3: Verify the template file is used (not inline text)
- if !strings.Contains(lockStr, "/opt/gh-aw/prompts/cache_memory_prompt.md") {
- t.Error("Expected '/opt/gh-aw/prompts/cache_memory_prompt.md' reference in generated workflow")
+ if !strings.Contains(lockStr, GhAwHome+"/prompts/cache_memory_prompt.md") {
+ t.Errorf("Expected '%s/prompts/cache_memory_prompt.md' reference in generated workflow", GhAwHome)
}
// Test 4: Verify the instruction mentions persistent cache
@@ -412,7 +412,7 @@ This is a test workflow with playwright enabled.
}
// Test 2: Verify the cat command for playwright prompt file is included
- if !strings.Contains(lockStr, "cat \"/opt/gh-aw/prompts/playwright_prompt.md\"") {
+ if !strings.Contains(lockStr, "cat \"${GH_AW_HOME}/prompts/playwright_prompt.md\"") {
t.Error("Expected cat command for playwright prompt file in generated workflow")
}
@@ -589,7 +589,7 @@ This is a test workflow with issue_comment trigger.
}
// Test 2: Verify the cat command for PR context prompt file is included
- if !strings.Contains(lockStr, "cat \"/opt/gh-aw/prompts/pr_context_prompt.md\"") {
+ if !strings.Contains(lockStr, "cat \"${GH_AW_HOME}/prompts/pr_context_prompt.md\"") {
t.Error("Expected cat command for PR context prompt file in generated workflow")
}
@@ -750,3 +750,101 @@ This is a test workflow without contents read permission.
t.Logf("Successfully verified PR context instructions are NOT included without contents permission")
}
+
+// ============================================================================
+// Agentic Workflows Guide Prompt Tests
+// ============================================================================
+
+func TestAgenticWorkflowsGuideIncludedWhenEnabled(t *testing.T) {
+ tmpDir, err := os.MkdirTemp("", "gh-aw-agentic-guide-test-*")
+ if err != nil {
+ t.Fatalf("Failed to create temp dir: %v", err)
+ }
+ defer os.RemoveAll(tmpDir)
+
+ testFile := filepath.Join(tmpDir, "test-workflow.md")
+ testContent := `---
+on: schedule daily
+engine: claude
+tools:
+ agentic-workflows:
+permissions:
+ actions: read
+---
+
+# Test Workflow with Agentic Workflows
+
+This workflow uses the agentic-workflows MCP server.
+`
+
+ if err := os.WriteFile(testFile, []byte(testContent), 0644); err != nil {
+ t.Fatalf("Failed to create test workflow: %v", err)
+ }
+
+ compiler := NewCompiler()
+ if err := compiler.CompileWorkflow(testFile); err != nil {
+ t.Fatalf("Failed to compile workflow: %v", err)
+ }
+
+ lockFile := stringutil.MarkdownToLockFile(testFile)
+ lockContent, err := os.ReadFile(lockFile)
+ if err != nil {
+ t.Fatalf("Failed to read generated lock file: %v", err)
+ }
+
+ lockStr := string(lockContent)
+
+ if !strings.Contains(lockStr, "- name: Create prompt with built-in context") {
+ t.Error("Expected 'Create prompt with built-in context' step in generated workflow")
+ }
+
+ if !strings.Contains(lockStr, "cat \"/opt/gh-aw/prompts/agentic_workflows_guide.md\"") {
+ t.Error("Expected cat command for agentic_workflows_guide.md in generated workflow")
+ }
+
+ t.Logf("Successfully verified agentic-workflows guide is included when agentic-workflows tool is enabled")
+}
+
+func TestAgenticWorkflowsGuideNotIncludedWhenDisabled(t *testing.T) {
+ tmpDir, err := os.MkdirTemp("", "gh-aw-no-agentic-guide-test-*")
+ if err != nil {
+ t.Fatalf("Failed to create temp dir: %v", err)
+ }
+ defer os.RemoveAll(tmpDir)
+
+ testFile := filepath.Join(tmpDir, "test-workflow.md")
+ testContent := `---
+on: push
+engine: codex
+tools:
+ github:
+---
+
+# Test Workflow without Agentic Workflows
+
+This workflow does not use the agentic-workflows MCP server.
+`
+
+ if err := os.WriteFile(testFile, []byte(testContent), 0644); err != nil {
+ t.Fatalf("Failed to create test workflow: %v", err)
+ }
+
+ compiler := NewCompiler()
+ if err := compiler.CompileWorkflow(testFile); err != nil {
+ t.Fatalf("Failed to compile workflow: %v", err)
+ }
+
+ lockFile := stringutil.MarkdownToLockFile(testFile)
+ lockContent, err := os.ReadFile(lockFile)
+ if err != nil {
+ t.Fatalf("Failed to read generated lock file: %v", err)
+ }
+
+ lockStr := string(lockContent)
+
+ if strings.Contains(lockStr, "agentic_workflows_guide.md") {
+ t.Error("Did not expect 'agentic_workflows_guide.md' reference in workflow without agentic-workflows tool")
+ }
+
+ t.Logf("Successfully verified agentic-workflows guide is NOT included when agentic-workflows tool is disabled")
+}
diff --git a/pkg/workflow/reaction_none_test.go b/pkg/workflow/reaction_none_test.go
index a79021387e2..6883805271c 100644
--- a/pkg/workflow/reaction_none_test.go
+++ b/pkg/workflow/reaction_none_test.go
@@ -17,12 +17,13 @@ func TestCommandWorkflowWithReactionNone(t *testing.T) {
// Create temporary directory for test files
tmpDir := testutil.TempDir(t, "reaction-none-test")
- // Create a test markdown file with command and reaction: none
+ // Create a test markdown file with command and reaction: none and status-comment: false
testContent := `---
on:
command:
name: test-bot
reaction: none
+ status-comment: false
permissions:
contents: read
issues: read
@@ -148,6 +149,11 @@ Test command workflow with default (eyes) reaction.
t.Errorf("Expected AIReaction to default to 'eyes' for command workflows, got '%s'", workflowData.AIReaction)
}
+ // Verify StatusComment defaults to true for command workflows
+ if workflowData.StatusComment == nil || !*workflowData.StatusComment {
+ t.Error("Expected StatusComment to default to true for command workflows")
+ }
+
// Compile the workflow
if err := compiler.CompileWorkflow(testFile); err != nil {
t.Fatalf("Failed to compile workflow: %v", err)
diff --git a/pkg/workflow/redact_secrets.go b/pkg/workflow/redact_secrets.go
index 95a3e5a1b05..b9485b2c7cf 100644
--- a/pkg/workflow/redact_secrets.go
+++ b/pkg/workflow/redact_secrets.go
@@ -79,9 +79,9 @@ func (c *Compiler) generateSecretRedactionStep(yaml *strings.Builder, yamlConten
// Load redact_secrets script from external file
// Use setupGlobals helper to attach GitHub Actions builtin objects to global scope
- yaml.WriteString(" const { setupGlobals } = require('" + SetupActionDestination + "/setup_globals.cjs');\n")
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
- yaml.WriteString(" const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/redact_secrets.cjs") + ");\n")
yaml.WriteString(" await main();\n")
// Add environment variables
diff --git a/pkg/workflow/repo_memory.go b/pkg/workflow/repo_memory.go
index 2e37ed9e92a..11b3ed4c42b 100644
--- a/pkg/workflow/repo_memory.go
+++ b/pkg/workflow/repo_memory.go
@@ -543,7 +543,7 @@ func generateRepoMemorySteps(builder *strings.Builder, data *WorkflowData) {
fmt.Fprintf(builder, " TARGET_REPO: %s\n", targetRepo)
fmt.Fprintf(builder, " MEMORY_DIR: %s\n", memoryDir)
fmt.Fprintf(builder, " CREATE_ORPHAN: %t\n", memory.CreateOrphan)
- builder.WriteString(" run: bash /opt/gh-aw/actions/clone_repo_memory_branch.sh\n")
+ builder.WriteString(" run: bash " + GhAwHome + "/actions/clone_repo_memory_branch.sh\n")
}
}
@@ -666,16 +666,16 @@ func (c *Compiler) buildPushRepoMemoryJob(data *WorkflowData, threatDetectionEna
if useRequire {
// Use require() to load script from copied files using setup_globals helper
- step.WriteString(" const { setupGlobals } = require('" + SetupActionDestination + "/setup_globals.cjs');\n")
+ step.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
step.WriteString(" setupGlobals(core, github, context, exec, io);\n")
- step.WriteString(" const { main } = require('" + SetupActionDestination + "/push_repo_memory.cjs');\n")
+ step.WriteString(" const { main } = require(" + JsRequireGhAw("actions/push_repo_memory.cjs") + ");\n")
step.WriteString(" await main();\n")
} else {
// Inline JavaScript: Attach GitHub Actions builtin objects to global scope before script execution
- step.WriteString(" const { setupGlobals } = require('" + SetupActionDestination + "/setup_globals.cjs');\n")
+ step.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
step.WriteString(" setupGlobals(core, github, context, exec, io);\n")
// Add the JavaScript script with proper indentation
- formattedScript := FormatJavaScriptForYAML("const { main } = require('/opt/gh-aw/actions/push_repo_memory.cjs'); await main();")
+ formattedScript := FormatJavaScriptForYAML("const { main } = require(" + JsRequireGhAw("actions/push_repo_memory.cjs") + "); await main();")
for _, line := range formattedScript {
step.WriteString(line)
}
@@ -713,6 +713,7 @@ func (c *Compiler) buildPushRepoMemoryJob(data *WorkflowData, threatDetectionEna
If: jobCondition,
Permissions: "permissions:\n contents: write",
Concurrency: concurrency,
+ Env: map[string]string{"GH_AW_HOME": GhAwHomeExprDefault},
Needs: []string{"agent"}, // Detection dependency added by caller if needed
Steps: steps,
Outputs: outputs,
diff --git a/pkg/workflow/repo_memory_integration_test.go b/pkg/workflow/repo_memory_integration_test.go
index 0a977810ff4..c79c634917f 100644
--- a/pkg/workflow/repo_memory_integration_test.go
+++ b/pkg/workflow/repo_memory_integration_test.go
@@ -243,7 +243,7 @@ This workflow has file validation.
}
// Check that push_repo_memory.cjs is being required (not inlined)
- if !strings.Contains(lockFile, "require('/opt/gh-aw/actions/push_repo_memory.cjs')") {
+ if !strings.Contains(lockFile, "require(process.env.GH_AW_HOME + '/actions/push_repo_memory.cjs')") {
t.Error("Expected push_repo_memory script to be loaded via require")
}
@@ -360,7 +360,7 @@ This workflow tests GitHub Enterprise support.
}
// Check for the shell script that uses GITHUB_SERVER_URL
- if !strings.Contains(lockFile, "bash /opt/gh-aw/actions/clone_repo_memory_branch.sh") {
+ if !strings.Contains(lockFile, "bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh") {
t.Error("Expected clone_repo_memory_branch.sh script invocation")
}
}
diff --git a/pkg/workflow/repo_memory_test.go b/pkg/workflow/repo_memory_test.go
index 85658f85515..efc8953178c 100644
--- a/pkg/workflow/repo_memory_test.go
+++ b/pkg/workflow/repo_memory_test.go
@@ -222,7 +222,7 @@ func TestRepoMemoryStepsGeneration(t *testing.T) {
}
// Check for script call
- if !strings.Contains(output, "bash /opt/gh-aw/actions/clone_repo_memory_branch.sh") {
+ if !strings.Contains(output, "bash ${GH_AW_HOME}/actions/clone_repo_memory_branch.sh") {
t.Error("Expected clone_repo_memory_branch.sh script call")
}
diff --git a/pkg/workflow/runtime_step_generator.go b/pkg/workflow/runtime_step_generator.go
index 32023d401b4..5f132a7efca 100644
--- a/pkg/workflow/runtime_step_generator.go
+++ b/pkg/workflow/runtime_step_generator.go
@@ -4,7 +4,6 @@ import (
"fmt"
"maps"
"sort"
- "strconv"
"strings"
"github.com/github/gh-aw/pkg/logger"
@@ -158,55 +157,6 @@ func generateSetupStep(req *RuntimeRequirement) GitHubActionStep {
return step
}
-// formatYAMLValue formats a value for YAML output
-func formatYAMLValue(value any) string {
- switch v := value.(type) {
- case string:
- // Quote strings if they contain special characters or look like non-string types
- if v == "true" || v == "false" || v == "null" {
- return fmt.Sprintf("'%s'", v)
- }
- // Check if it's a number
- if _, err := fmt.Sscanf(v, "%f", new(float64)); err == nil {
- return fmt.Sprintf("'%s'", v)
- }
- // Return as-is for simple strings, quote for complex ones
- return fmt.Sprintf("'%s'", v)
- case bool:
- if v {
- return "true"
- }
- return "false"
- case int:
- return strconv.Itoa(v)
- case int8:
- return strconv.Itoa(int(v))
- case int16:
- return strconv.Itoa(int(v))
- case int32:
- return strconv.Itoa(int(v))
- case int64:
- return strconv.FormatInt(v, 10)
- case uint:
- return strconv.FormatUint(uint64(v), 10)
- case uint8:
- return strconv.FormatUint(uint64(v), 10)
- case uint16:
- return strconv.FormatUint(uint64(v), 10)
- case uint32:
- return strconv.FormatUint(uint64(v), 10)
- case uint64:
- return strconv.FormatUint(v, 10)
- case float32:
- return fmt.Sprintf("%v", v)
- case float64:
- return fmt.Sprintf("%v", v)
- default:
- // For other types, convert to string and quote
- return fmt.Sprintf("'%v'", v)
- }
-}
-
// GenerateMultiSecretValidationStep creates a GitHub Actions step that validates at least one
// of multiple secrets is available.
// secretNames: slice of secret names to validate (e.g., []string{"CODEX_API_KEY", "OPENAI_API_KEY"})
@@ -235,7 +185,7 @@ func GenerateMultiSecretValidationStep(secretNames []string, engineName, docsURL
stepLines := []string{
stepName,
" id: validate-secret",
- " run: /opt/gh-aw/actions/validate_multi_secret.sh " + scriptArgsStr,
+ " run: " + GhAwHome + "/actions/validate_multi_secret.sh " + scriptArgsStr,
" env:",
}
diff --git a/pkg/workflow/runtime_validation.go b/pkg/workflow/runtime_validation.go
index a3f2b769125..f725af5e0b7 100644
--- a/pkg/workflow/runtime_validation.go
+++ b/pkg/workflow/runtime_validation.go
@@ -246,8 +246,17 @@ func (c *Compiler) validateRuntimePackages(workflowData *WorkflowData) error {
// Validate npx packages used in the workflow
runtimeValidationLog.Print("Validating npx packages")
if err := c.validateNpxPackages(workflowData); err != nil {
- runtimeValidationLog.Printf("Npx package validation failed: %v", err)
- errors = append(errors, err.Error())
+ if isErrNpmNotAvailable(err) {
+ // npm is not installed on this system — treat as a warning, not an error.
+ // The workflow may still compile and run successfully in environments
+ // that have npm (e.g., GitHub Actions).
+ runtimeValidationLog.Print("npm not available, skipping npx package validation")
+ fmt.Fprintln(os.Stderr, console.FormatWarningMessage("npm not found, skipping npx package validation"))
+ c.IncrementWarningCount()
+ } else {
+ runtimeValidationLog.Printf("Npx package validation failed: %v", err)
+ errors = append(errors, err.Error())
+ }
}
case "python":
// Validate pip packages used in the workflow
diff --git a/pkg/workflow/safe_jobs.go b/pkg/workflow/safe_jobs.go
index 8c43aaf52be..cfd9a2b4599 100644
--- a/pkg/workflow/safe_jobs.go
+++ b/pkg/workflow/safe_jobs.go
@@ -225,19 +225,19 @@ func (c *Compiler) buildSafeJobs(data *WorkflowData, threatDetectionEnabled bool
agentArtifactPrefix := artifactPrefixExprForAgentDownstreamJob(data)
downloadSteps := buildArtifactDownloadSteps(ArtifactDownloadConfig{
ArtifactName: agentArtifactPrefix + constants.AgentArtifactName,
- DownloadPath: "/opt/gh-aw/safe-jobs/",
+ DownloadPath: GhAwHomeExpr + "/safe-jobs/",
SetupEnvStep: false, // We'll handle env vars separately to add job-specific ones
StepName: "Download agent output artifact",
})
steps = append(steps, downloadSteps...)
// the download artifacts always creates a folder, then unpacks in that folder
- agentOutputArtifactFilename := "/opt/gh-aw/safe-jobs/" + constants.AgentOutputFilename
+ agentOutputArtifactFilename := GhAwHomeExpr + "/safe-jobs/" + constants.AgentOutputFilename
// Add environment variables step with GH_AW_AGENT_OUTPUT and job-specific env vars
steps = append(steps, " - name: Setup Safe Job Environment Variables\n")
steps = append(steps, " run: |\n")
- steps = append(steps, " find \"/opt/gh-aw/safe-jobs/\" -type f -print\n")
+ steps = append(steps, " find \""+GhAwHomeExpr+"/safe-jobs/\" -type f -print\n")
// Configure GH_AW_AGENT_OUTPUT to point to downloaded artifact file
steps = append(steps, fmt.Sprintf(" echo \"GH_AW_AGENT_OUTPUT=%s\" >> \"$GITHUB_ENV\"\n", agentOutputArtifactFilename))
diff --git a/pkg/workflow/safe_outputs_call_workflow_test.go b/pkg/workflow/safe_outputs_call_workflow_test.go
index 36aa0dfde58..d97ed278f47 100644
--- a/pkg/workflow/safe_outputs_call_workflow_test.go
+++ b/pkg/workflow/safe_outputs_call_workflow_test.go
@@ -383,6 +383,84 @@ func TestCallWorkflowConclusionDependencies(t *testing.T) {
assert.True(t, workerBExists, "call-worker-b job should exist")
}
+// TestBuildCallWorkflowJobs_ForwardsDeclaredInputsFromPayload tests that declared
+// workflow_call inputs (except 'payload') are forwarded as fromJSON expressions
+// in the generated with: block.
+func TestBuildCallWorkflowJobs_ForwardsDeclaredInputsFromPayload(t *testing.T) {
+ tmpDir := t.TempDir()
+ workflowsDir := filepath.Join(tmpDir, ".github", "workflows")
+ require.NoError(t, os.MkdirAll(workflowsDir, 0755))
+
+ // Worker declares environment, version, and the canonical payload input.
+ workerContent := `name: Worker
+on:
+ workflow_call:
+ inputs:
+ payload:
+ type: string
+ required: false
+ environment:
+ description: Target environment
+ type: string
+ required: true
+ version:
+ description: Package version
+ type: string
+ required: false
+jobs:
+ work:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo "Working"
+`
+ require.NoError(t, os.WriteFile(filepath.Join(workflowsDir, "worker-a.lock.yml"), []byte(workerContent), 0644))
+
+ gatewayFile := filepath.Join(workflowsDir, "gateway.md")
+ require.NoError(t, os.WriteFile(gatewayFile, []byte("# Gateway"), 0644))
+
+ compiler := NewCompilerWithVersion("1.0.0")
+ workflowData := &WorkflowData{
+ SafeOutputs: &SafeOutputsConfig{
+ CallWorkflow: &CallWorkflowConfig{
+ BaseSafeOutputConfig: BaseSafeOutputConfig{Max: strPtr("1")},
+ Workflows: []string{"worker-a"},
+ WorkflowFiles: map[string]string{
+ "worker-a": "./.github/workflows/worker-a.lock.yml",
+ },
+ },
+ },
+ }
+
+ jobNames, err := compiler.buildCallWorkflowJobs(workflowData, gatewayFile)
+ require.NoError(t, err, "Should not error building call-workflow jobs")
+ require.Equal(t, []string{"call-worker-a"}, jobNames)
+
+ job, exists := compiler.jobManager.GetJob("call-worker-a")
+ require.True(t, exists, "call-worker-a job should exist")
+
+ // payload is always forwarded as the canonical transport
+ assert.Equal(t, "${{ needs.safe_outputs.outputs.call_workflow_payload }}", job.With["payload"],
+ "Should always include payload")
+
+ // environment and version should be derived from the payload
+ assert.Equal(t,
+ "${{ fromJSON(needs.safe_outputs.outputs.call_workflow_payload).environment }}",
+ job.With["environment"],
+ "Should forward environment from payload")
+ assert.Equal(t,
+ "${{ fromJSON(needs.safe_outputs.outputs.call_workflow_payload).version }}",
+ job.With["version"],
+ "Should forward version from payload")
+
+ // payload must appear exactly once as the canonical (non-fromJSON) transport entry.
+ // Verify it is not duplicated as a fromJSON expression.
+ _, hasPayloadFromJSON := job.With["payload"]
+ assert.True(t, hasPayloadFromJSON, "payload key must be present")
+ payloadVal, _ := job.With["payload"].(string)
+ assert.NotContains(t, payloadVal, "fromJSON",
+ "payload canonical entry must be the raw step output, not a fromJSON expression")
+}
+
// TestExtractWorkflowCallInputsFromParsed tests the parsing of workflow_call inputs
// from an already-parsed workflow map
func TestExtractWorkflowCallInputsFromParsed(t *testing.T) {
diff --git a/pkg/workflow/safe_outputs_config.go b/pkg/workflow/safe_outputs_config.go
index 38b4f63d820..61ce26df1cf 100644
--- a/pkg/workflow/safe_outputs_config.go
+++ b/pkg/workflow/safe_outputs_config.go
@@ -170,7 +170,7 @@ func (c *Compiler) extractSafeOutputsConfig(frontmatter map[string]any) *SafeOut
config.AutofixCodeScanningAlert = autofixCodeScanningAlertConfig
}
- // Parse allowed-domains configuration
+ // Parse allowed-domains configuration (additional domains, unioned with network.allowed; supports ecosystem identifiers)
if allowedDomains, exists := outputMap["allowed-domains"]; exists {
if domainsArray, ok := allowedDomains.([]any); ok {
var domainStrings []string
diff --git a/pkg/workflow/safe_outputs_env.go b/pkg/workflow/safe_outputs_env.go
new file mode 100644
index 00000000000..ce3d88af53b
--- /dev/null
+++ b/pkg/workflow/safe_outputs_env.go
@@ -0,0 +1,292 @@
+package workflow
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/github/gh-aw/pkg/logger"
+)
+
+var safeOutputsEnvLog = logger.New("workflow:safe_outputs_env")
+
+// ========================================
+// Safe Output Environment Variables
+// ========================================
+
+// applySafeOutputEnvToMap adds safe-output related environment variables to an env map
+// This extracts the duplicated safe-output env setup logic across all engines (copilot, codex, claude, custom)
+func applySafeOutputEnvToMap(env map[string]string, data *WorkflowData) {
+ if data.SafeOutputs == nil {
+ return
+ }
+
+ safeOutputsEnvLog.Printf("Applying safe output env vars: trial_mode=%t, staged=%t", data.TrialMode, data.SafeOutputs.Staged)
+
+ env["GH_AW_SAFE_OUTPUTS"] = "${{ env.GH_AW_SAFE_OUTPUTS }}"
+
+ // Add staged flag if specified
+ if data.TrialMode || data.SafeOutputs.Staged {
+ env["GH_AW_SAFE_OUTPUTS_STAGED"] = "true"
+ }
+ if data.TrialMode && data.TrialLogicalRepo != "" {
+ env["GH_AW_TARGET_REPO_SLUG"] = data.TrialLogicalRepo
+ }
+
+ // Add branch name if upload assets is configured
+ if data.SafeOutputs.UploadAssets != nil {
+ safeOutputsEnvLog.Printf("Adding upload assets env vars: branch=%s", data.SafeOutputs.UploadAssets.BranchName)
+ env["GH_AW_ASSETS_BRANCH"] = fmt.Sprintf("%q", data.SafeOutputs.UploadAssets.BranchName)
+ env["GH_AW_ASSETS_MAX_SIZE_KB"] = strconv.Itoa(data.SafeOutputs.UploadAssets.MaxSizeKB)
+ env["GH_AW_ASSETS_ALLOWED_EXTS"] = fmt.Sprintf("%q", strings.Join(data.SafeOutputs.UploadAssets.AllowedExts, ","))
+ }
+}
+
+// buildWorkflowMetadataEnvVars builds workflow name and source environment variables
+// This extracts the duplicated workflow metadata setup logic from safe-output job builders
+func buildWorkflowMetadataEnvVars(workflowName string, workflowSource string) []string {
+ var customEnvVars []string
+
+ // Add workflow name
+ customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_WORKFLOW_NAME: %q\n", workflowName))
+
+ // Add workflow source and source URL if present
+ if workflowSource != "" {
+ customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_WORKFLOW_SOURCE: %q\n", workflowSource))
+ sourceURL := buildSourceURL(workflowSource)
+ if sourceURL != "" {
+ customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_WORKFLOW_SOURCE_URL: %q\n", sourceURL))
+ }
+ }
+
+ return customEnvVars
+}
+
+// buildWorkflowMetadataEnvVarsWithTrackerID builds workflow metadata env vars including tracker-id
+func buildWorkflowMetadataEnvVarsWithTrackerID(workflowName string, workflowSource string, trackerID string) []string {
+ customEnvVars := buildWorkflowMetadataEnvVars(workflowName, workflowSource)
+
+ // Add tracker-id if present
+ if trackerID != "" {
+ customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_TRACKER_ID: %q\n", trackerID))
+ }
+
+ return customEnvVars
+}
+
+// buildSafeOutputJobEnvVars builds environment variables for safe-output jobs with staged/target repo handling
+// This extracts the duplicated env setup logic in safe-output job builders (create_issue, add_comment, etc.)
+func buildSafeOutputJobEnvVars(trialMode bool, trialLogicalRepoSlug string, staged bool, targetRepoSlug string) []string {
+ var customEnvVars []string
+
+ // Pass the staged flag if it's set to true
+ if trialMode || staged {
+ safeOutputsEnvLog.Printf("Setting staged flag: trial_mode=%t, staged=%t", trialMode, staged)
+ customEnvVars = append(customEnvVars, " GH_AW_SAFE_OUTPUTS_STAGED: \"true\"\n")
+ }
+
+ // Set GH_AW_TARGET_REPO_SLUG - prefer target-repo config over trial target repo
+ if targetRepoSlug != "" {
+ safeOutputsEnvLog.Printf("Setting target repo slug from config: %s", targetRepoSlug)
+ customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_TARGET_REPO_SLUG: %q\n", targetRepoSlug))
+ } else if trialMode && trialLogicalRepoSlug != "" {
+ safeOutputsEnvLog.Printf("Setting target repo slug from trial mode: %s", trialLogicalRepoSlug)
+ customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_TARGET_REPO_SLUG: %q\n", trialLogicalRepoSlug))
+ }
+
+ return customEnvVars
+}
+
+// buildStandardSafeOutputEnvVars builds the standard set of environment variables
+// that all safe-output job builders need: metadata + staged/target repo handling
+// This reduces duplication in safe-output job builders
+func (c *Compiler) buildStandardSafeOutputEnvVars(data *WorkflowData, targetRepoSlug string) []string {
+ var customEnvVars []string
+
+ // Add workflow metadata (name, source, and tracker-id)
+ customEnvVars = append(customEnvVars, buildWorkflowMetadataEnvVarsWithTrackerID(data.Name, data.Source, data.TrackerID)...)
+
+ // Add engine metadata (id, version, model) for XML comment marker
+ customEnvVars = append(customEnvVars, buildEngineMetadataEnvVars(data.EngineConfig)...)
+
+ // Add common safe output job environment variables (staged/target repo)
+ customEnvVars = append(customEnvVars, buildSafeOutputJobEnvVars(
+ c.trialMode,
+ c.trialLogicalRepoSlug,
+ data.SafeOutputs.Staged,
+ targetRepoSlug,
+ )...)
+
+ // Add messages config if present
+ if data.SafeOutputs.Messages != nil {
+ messagesJSON, err := serializeMessagesConfig(data.SafeOutputs.Messages)
+ if err != nil {
+ safeOutputsEnvLog.Printf("Warning: failed to serialize messages config: %v", err)
+ } else if messagesJSON != "" {
+ customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_SAFE_OUTPUT_MESSAGES: %q\n", messagesJSON))
+ }
+ }
+
+ return customEnvVars
+}
+
+// buildStepLevelSafeOutputEnvVars builds environment variables for consolidated safe output steps
+// This excludes variables that are already set at the job level in consolidated jobs
+func (c *Compiler) buildStepLevelSafeOutputEnvVars(data *WorkflowData, targetRepoSlug string) []string {
+ var customEnvVars []string
+
+ // Only add target repo slug if it's different from the job-level setting
+ // (i.e., this step has a specific target-repo config that overrides the global trial mode target)
+ if targetRepoSlug != "" {
+ // Step-specific target repo overrides job-level setting
+ customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_TARGET_REPO_SLUG: %q\n", targetRepoSlug))
+ } else if !c.trialMode && data.SafeOutputs.Staged {
+ // Step needs staged flag but there's no job-level target repo (not in trial mode)
+ // Job level only sets this if trialMode is true
+ customEnvVars = append(customEnvVars, " GH_AW_SAFE_OUTPUTS_STAGED: \"true\"\n")
+ }
+
+ // Note: The following are now set at job level and should NOT be included here:
+ // - GH_AW_WORKFLOW_NAME
+ // - GH_AW_WORKFLOW_SOURCE
+ // - GH_AW_WORKFLOW_SOURCE_URL
+ // - GH_AW_TRACKER_ID
+ // - GH_AW_ENGINE_ID
+ // - GH_AW_ENGINE_VERSION
+ // - GH_AW_ENGINE_MODEL
+ // - GH_AW_SAFE_OUTPUTS_STAGED (if in trial mode)
+ // - GH_AW_TARGET_REPO_SLUG (if in trial mode and no step override)
+ // - GH_AW_SAFE_OUTPUT_MESSAGES
+
+ return customEnvVars
+}
+
+// buildEngineMetadataEnvVars builds engine metadata environment variables (id, version, model)
+// These are used by the JavaScript footer generation to create XML comment markers for traceability
+func buildEngineMetadataEnvVars(engineConfig *EngineConfig) []string {
+ var customEnvVars []string
+
+ if engineConfig == nil {
+ return customEnvVars
+ }
+
+ safeOutputsEnvLog.Printf("Building engine metadata env vars: id=%s, version=%s", engineConfig.ID, engineConfig.Version)
+
+ // Add engine ID if present
+ if engineConfig.ID != "" {
+ customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_ENGINE_ID: %q\n", engineConfig.ID))
+ }
+
+ // Add engine version if present
+ if engineConfig.Version != "" {
+ customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_ENGINE_VERSION: %q\n", engineConfig.Version))
+ }
+
+ // Add engine model if present
+ if engineConfig.Model != "" {
+ customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_ENGINE_MODEL: %q\n", engineConfig.Model))
+ }
+
+ return customEnvVars
+}
+
+// ========================================
+// Safe Output Environment Helpers
+// ========================================
+
+// addCustomSafeOutputEnvVars adds custom environment variables to safe output job steps
+func (c *Compiler) addCustomSafeOutputEnvVars(steps *[]string, data *WorkflowData) {
+ if data.SafeOutputs != nil && len(data.SafeOutputs.Env) > 0 {
+ for key, value := range data.SafeOutputs.Env {
+ *steps = append(*steps, fmt.Sprintf(" %s: %s\n", key, value))
+ }
+ }
+}
+
+// addSafeOutputGitHubTokenForConfig adds github-token to the with section, preferring per-config token over global
+// Uses precedence: config token > safe-outputs global github-token > GH_AW_GITHUB_TOKEN || GITHUB_TOKEN
+func (c *Compiler) addSafeOutputGitHubTokenForConfig(steps *[]string, data *WorkflowData, configToken string) {
+ var safeOutputsToken string
+ if data.SafeOutputs != nil {
+ safeOutputsToken = data.SafeOutputs.GitHubToken
+ }
+
+ // If app is configured, use app token
+ if data.SafeOutputs != nil && data.SafeOutputs.GitHubApp != nil {
+ *steps = append(*steps, " github-token: ${{ steps.safe-outputs-app-token.outputs.token }}\n")
+ return
+ }
+
+ // Choose the first non-empty custom token for precedence
+ effectiveCustomToken := configToken
+ if effectiveCustomToken == "" {
+ effectiveCustomToken = safeOutputsToken
+ }
+
+ // Get effective token
+ effectiveToken := getEffectiveSafeOutputGitHubToken(effectiveCustomToken)
+ *steps = append(*steps, fmt.Sprintf(" github-token: %s\n", effectiveToken))
+}
+
+// addSafeOutputCopilotGitHubTokenForConfig adds github-token to the with section for Copilot-related operations
+// Uses precedence: config token > safe-outputs global github-token > COPILOT_GITHUB_TOKEN
+func (c *Compiler) addSafeOutputCopilotGitHubTokenForConfig(steps *[]string, data *WorkflowData, configToken string) {
+ var safeOutputsToken string
+ if data.SafeOutputs != nil {
+ safeOutputsToken = data.SafeOutputs.GitHubToken
+ }
+
+ // If app is configured, use app token
+ if data.SafeOutputs != nil && data.SafeOutputs.GitHubApp != nil {
+ *steps = append(*steps, " github-token: ${{ steps.safe-outputs-app-token.outputs.token }}\n")
+ return
+ }
+
+ // Choose the first non-empty custom token for precedence
+ effectiveCustomToken := configToken
+ if effectiveCustomToken == "" {
+ effectiveCustomToken = safeOutputsToken
+ }
+
+ // Get effective token
+ effectiveToken := getEffectiveCopilotRequestsToken(effectiveCustomToken)
+ *steps = append(*steps, fmt.Sprintf(" github-token: %s\n", effectiveToken))
+}
+
+// addSafeOutputAgentGitHubTokenForConfig adds github-token to the with section for agent assignment operations
+// Uses precedence: config token > safe-outputs token > GH_AW_AGENT_TOKEN || GH_AW_GITHUB_TOKEN || GITHUB_TOKEN
+// This is specifically for assign-to-agent operations which require elevated permissions.
+//
+// Note: GitHub App tokens are intentionally NOT used here, even when github-app: is configured.
+// The Copilot assignment API only accepts PATs (fine-grained or classic), not GitHub App
+// installation tokens. Callers must provide an explicit github-token or rely on GH_AW_AGENT_TOKEN.
+func (c *Compiler) addSafeOutputAgentGitHubTokenForConfig(steps *[]string, data *WorkflowData, configToken string) {
+ // Get safe-outputs level token
+ var safeOutputsToken string
+ if data.SafeOutputs != nil {
+ safeOutputsToken = data.SafeOutputs.GitHubToken
+ }
+
+ // Choose the first non-empty custom token for precedence
+ effectiveCustomToken := configToken
+ if effectiveCustomToken == "" {
+ effectiveCustomToken = safeOutputsToken
+ }
+
+ // Get effective token - falls back to ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ // when no explicit token is provided. GitHub App tokens are never used here because the
+ // Copilot assignment API rejects them.
+ effectiveToken := getEffectiveCopilotCodingAgentGitHubToken(effectiveCustomToken)
+ *steps = append(*steps, fmt.Sprintf(" github-token: %s\n", effectiveToken))
+}
+
+// buildAllowedReposEnvVar builds an allowed-repos environment variable line for safe-output jobs.
+// envVarName should be the full env var name like "GH_AW_ALLOWED_REPOS".
+// Returns an empty slice if allowedRepos is empty.
+func buildAllowedReposEnvVar(envVarName string, allowedRepos []string) []string {
+ if len(allowedRepos) == 0 {
+ return nil
+ }
+ reposStr := strings.Join(allowedRepos, ",")
+ return []string{fmt.Sprintf(" %s: %q\n", envVarName, reposStr)}
+}
diff --git a/pkg/workflow/safe_outputs_generation.go b/pkg/workflow/safe_outputs_generation.go
deleted file mode 100644
index 8c66280a878..00000000000
--- a/pkg/workflow/safe_outputs_generation.go
+++ /dev/null
@@ -1,7 +0,0 @@
-// Package workflow - safe outputs generation has been refactored into focused modules:
-// - safe_outputs_config_generation.go: main config generation and helpers
-// - safe_outputs_state.go: state inspection and validation (reflection-based)
-// - safe_outputs_runtime.go: runner/workspace configuration
-// - safe_outputs_tools_filtering.go: tool enumeration and filtering
-// - safe_outputs_dispatch.go: dispatch-workflow file mapping
-package workflow
diff --git a/pkg/workflow/safe_outputs_jobs.go b/pkg/workflow/safe_outputs_jobs.go
index 1d2f9fab4b5..f5b5c3df901 100644
--- a/pkg/workflow/safe_outputs_jobs.go
+++ b/pkg/workflow/safe_outputs_jobs.go
@@ -1,11 +1,6 @@
package workflow
import (
- "fmt"
- "strconv"
- "strings"
-
- "github.com/github/gh-aw/pkg/constants"
"github.com/github/gh-aw/pkg/logger"
)
@@ -156,539 +151,3 @@ func (c *Compiler) buildSafeOutputJob(data *WorkflowData, config SafeOutputJobCo
return job, nil
}
-
-var safeOutputsStepsLog = logger.New("workflow:safe_outputs_steps")
-
-// ========================================
-// Safe Output Step Builders
-// ========================================
-
-// buildCustomActionStep creates a step that uses a custom action reference
-// instead of inline JavaScript via actions/github-script
-func (c *Compiler) buildCustomActionStep(data *WorkflowData, config GitHubScriptStepConfig, scriptName string) []string {
- safeOutputsStepsLog.Printf("Building custom action step: %s (scriptName=%s, actionMode=%s)", config.StepName, scriptName, c.actionMode)
-
- var steps []string
-
- // Get the action path from the script registry
- actionPath := DefaultScriptRegistry.GetActionPath(scriptName)
- if actionPath == "" {
- safeOutputsStepsLog.Printf("WARNING: No action path found for script %s, falling back to inline mode", scriptName)
- // Set ScriptFile for inline mode fallback
- config.ScriptFile = scriptName + ".cjs"
- return c.buildGitHubScriptStep(data, config)
- }
-
- // Resolve the action reference based on mode
- actionRef := c.resolveActionReference(actionPath, data)
- if actionRef == "" {
- safeOutputsStepsLog.Printf("WARNING: Could not resolve action reference for %s, falling back to inline mode", actionPath)
- // Set ScriptFile for inline mode fallback
- config.ScriptFile = scriptName + ".cjs"
- return c.buildGitHubScriptStep(data, config)
- }
-
- // Add artifact download steps before the custom action step.
- // In workflow_call context, use the per-invocation prefix to avoid artifact name clashes.
- // These steps are used in jobs that depend on the agent job (not activation), so use
- // the agent-downstream prefix expression.
- steps = append(steps, buildAgentOutputDownloadSteps(artifactPrefixExprForAgentDownstreamJob(data))...)
-
- // Step name and metadata
- steps = append(steps, fmt.Sprintf(" - name: %s\n", config.StepName))
- steps = append(steps, fmt.Sprintf(" id: %s\n", config.StepID))
- steps = append(steps, fmt.Sprintf(" uses: %s\n", actionRef))
-
- // Environment variables section
- steps = append(steps, " env:\n")
- steps = append(steps, " GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}\n")
- steps = append(steps, config.CustomEnvVars...)
- c.addCustomSafeOutputEnvVars(&steps, data)
-
- // With section for inputs (replaces github-token in actions/github-script)
- steps = append(steps, " with:\n")
-
- // Map github-token to token input for custom actions
- c.addCustomActionGitHubToken(&steps, data, config)
-
- return steps
-}
-
-// addCustomActionGitHubToken adds a GitHub token as action input.
-// The token precedence depends on the tokenType flags:
-// - UseCopilotCodingAgentToken: customToken > SafeOutputs.GitHubToken > GH_AW_AGENT_TOKEN || GH_AW_GITHUB_TOKEN || GITHUB_TOKEN
-// - UseCopilotRequestsToken: customToken > SafeOutputs.GitHubToken > COPILOT_GITHUB_TOKEN
-// - Default: customToken > SafeOutputs.GitHubToken > GH_AW_GITHUB_TOKEN || GITHUB_TOKEN
-func (c *Compiler) addCustomActionGitHubToken(steps *[]string, data *WorkflowData, config GitHubScriptStepConfig) {
- var token string
-
- // Get safe-outputs level token
- var safeOutputsToken string
- if data.SafeOutputs != nil {
- safeOutputsToken = data.SafeOutputs.GitHubToken
- }
-
- // Choose the first non-empty custom token for precedence
- effectiveCustomToken := config.CustomToken
- if effectiveCustomToken == "" {
- effectiveCustomToken = safeOutputsToken
- }
-
- // Agent token mode: use full precedence chain for agent assignment
- if config.UseCopilotCodingAgentToken {
- token = getEffectiveCopilotCodingAgentGitHubToken(effectiveCustomToken)
- } else if config.UseCopilotRequestsToken {
- // Copilot mode: use getEffectiveCopilotRequestsToken with safe-outputs token precedence
- token = getEffectiveCopilotRequestsToken(effectiveCustomToken)
- } else {
- // Standard mode: use safe output token chain
- token = getEffectiveSafeOutputGitHubToken(effectiveCustomToken)
- }
-
- *steps = append(*steps, fmt.Sprintf(" token: %s\n", token))
-}
-
-// GitHubScriptStepConfig holds configuration for building a GitHub Script step
-type GitHubScriptStepConfig struct {
- // Step metadata
- StepName string // e.g., "Create Output Issue"
- StepID string // e.g., "create_issue"
-
- // Main job reference for agent output
- MainJobName string
-
- // Environment variables specific to this safe output type
- // These are added after GH_AW_AGENT_OUTPUT
- CustomEnvVars []string
-
- // JavaScript script constant to format and include (for inline mode)
- Script string
-
- // ScriptFile is the .cjs filename to require (e.g., "noop.cjs")
- // If empty, Script will be inlined instead
- ScriptFile string
-
- // CustomToken configuration (passed to addSafeOutputGitHubTokenForConfig or addSafeOutputCopilotGitHubTokenForConfig)
- CustomToken string
-
- // UseCopilotRequestsToken indicates whether to use the Copilot token preference chain
- // custom token > COPILOT_GITHUB_TOKEN
- // This should be true for Copilot-related operations like creating agent tasks,
- // assigning copilot to issues, or adding copilot as PR reviewer
- UseCopilotRequestsToken bool
-
- // UseCopilotCodingAgentToken indicates whether to use the agent token preference chain
- // (config token > GH_AW_AGENT_TOKEN)
- // This should be true for agent assignment operations (assign-to-agent)
- UseCopilotCodingAgentToken bool
-}
-
-// buildGitHubScriptStep creates a GitHub Script step with common scaffolding
-// This extracts the repeated pattern found across safe output job builders
-func (c *Compiler) buildGitHubScriptStep(data *WorkflowData, config GitHubScriptStepConfig) []string {
- safeOutputsStepsLog.Printf("Building GitHub Script step: %s (useCopilotRequestsToken=%v, useCopilotCodingAgentToken=%v)", config.StepName, config.UseCopilotRequestsToken, config.UseCopilotCodingAgentToken)
-
- var steps []string
-
- // Add artifact download steps before the GitHub Script step.
- // In workflow_call context, use the per-invocation prefix to avoid artifact name clashes.
- // These steps are used in jobs that depend on the agent job (not activation), so use
- // the agent-downstream prefix expression.
- steps = append(steps, buildAgentOutputDownloadSteps(artifactPrefixExprForAgentDownstreamJob(data))...)
-
- // Step name and metadata
- steps = append(steps, fmt.Sprintf(" - name: %s\n", config.StepName))
- steps = append(steps, fmt.Sprintf(" id: %s\n", config.StepID))
- steps = append(steps, fmt.Sprintf(" uses: %s\n", GetActionPin("actions/github-script")))
-
- // Environment variables section
- steps = append(steps, " env:\n")
-
- // Read GH_AW_AGENT_OUTPUT from environment (set by artifact download step)
- // instead of directly from job outputs which may be masked by GitHub Actions
- steps = append(steps, " GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}\n")
-
- // Add custom environment variables specific to this safe output type
- steps = append(steps, config.CustomEnvVars...)
-
- // Add custom environment variables from safe-outputs.env
- c.addCustomSafeOutputEnvVars(&steps, data)
-
- // With section for github-token
- steps = append(steps, " with:\n")
- if config.UseCopilotCodingAgentToken {
- c.addSafeOutputAgentGitHubTokenForConfig(&steps, data, config.CustomToken)
- } else if config.UseCopilotRequestsToken {
- c.addSafeOutputCopilotGitHubTokenForConfig(&steps, data, config.CustomToken)
- } else {
- c.addSafeOutputGitHubTokenForConfig(&steps, data, config.CustomToken)
- }
-
- steps = append(steps, " script: |\n")
-
- // Use require() if ScriptFile is specified, otherwise inline the script
- if config.ScriptFile != "" {
- steps = append(steps, " const { setupGlobals } = require('"+SetupActionDestination+"/setup_globals.cjs');\n")
- steps = append(steps, " setupGlobals(core, github, context, exec, io);\n")
- steps = append(steps, fmt.Sprintf(" const { main } = require('"+SetupActionDestination+"/%s');\n", config.ScriptFile))
- steps = append(steps, " await main();\n")
- } else {
- // Add the formatted JavaScript script (inline)
- formattedScript := FormatJavaScriptForYAML(config.Script)
- steps = append(steps, formattedScript...)
- }
-
- return steps
-}
-
-// buildGitHubScriptStepWithoutDownload creates a GitHub Script step without artifact download steps
-// This is useful when multiple script steps are needed in the same job and artifact downloads
-// should only happen once at the beginning
-func (c *Compiler) buildGitHubScriptStepWithoutDownload(data *WorkflowData, config GitHubScriptStepConfig) []string {
- safeOutputsStepsLog.Printf("Building GitHub Script step without download: %s", config.StepName)
-
- var steps []string
-
- // Step name and metadata (no artifact download steps)
- steps = append(steps, fmt.Sprintf(" - name: %s\n", config.StepName))
- steps = append(steps, fmt.Sprintf(" id: %s\n", config.StepID))
- steps = append(steps, fmt.Sprintf(" uses: %s\n", GetActionPin("actions/github-script")))
-
- // Environment variables section
- steps = append(steps, " env:\n")
-
- // Read GH_AW_AGENT_OUTPUT from environment (set by artifact download step)
- // instead of directly from job outputs which may be masked by GitHub Actions
- steps = append(steps, " GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}\n")
-
- // Add custom environment variables specific to this safe output type
- steps = append(steps, config.CustomEnvVars...)
-
- // Add custom environment variables from safe-outputs.env
- c.addCustomSafeOutputEnvVars(&steps, data)
-
- // With section for github-token
- steps = append(steps, " with:\n")
- if config.UseCopilotCodingAgentToken {
- c.addSafeOutputAgentGitHubTokenForConfig(&steps, data, config.CustomToken)
- } else if config.UseCopilotRequestsToken {
- c.addSafeOutputCopilotGitHubTokenForConfig(&steps, data, config.CustomToken)
- } else {
- c.addSafeOutputGitHubTokenForConfig(&steps, data, config.CustomToken)
- }
-
- steps = append(steps, " script: |\n")
-
- // Use require() if ScriptFile is specified, otherwise inline the script
- if config.ScriptFile != "" {
- steps = append(steps, " const { setupGlobals } = require('"+SetupActionDestination+"/setup_globals.cjs');\n")
- steps = append(steps, " setupGlobals(core, github, context, exec, io);\n")
- steps = append(steps, fmt.Sprintf(" const { main } = require('"+SetupActionDestination+"/%s');\n", config.ScriptFile))
- steps = append(steps, " await main();\n")
- } else {
- // Add the formatted JavaScript script (inline)
- formattedScript := FormatJavaScriptForYAML(config.Script)
- steps = append(steps, formattedScript...)
- }
-
- return steps
-}
-
-// buildAgentOutputDownloadSteps creates steps to download the agent output artifact
-// and set the GH_AW_AGENT_OUTPUT environment variable for safe-output jobs.
-// GH_AW_AGENT_OUTPUT is only set when the artifact was actually downloaded successfully.
-// prefix is prepended to the artifact name; use empty string for non-workflow_call workflows.
-func buildAgentOutputDownloadSteps(prefix string) []string {
- return buildArtifactDownloadSteps(ArtifactDownloadConfig{
- ArtifactName: prefix + constants.AgentArtifactName, // Unified agent artifact (prefixed in workflow_call)
- ArtifactFilename: constants.AgentOutputFilename, // Filename inside the artifact directory
- DownloadPath: "/tmp/gh-aw/",
- SetupEnvStep: true,
- EnvVarName: "GH_AW_AGENT_OUTPUT",
- StepName: "Download agent output artifact",
- StepID: "download-agent-output",
- })
-}
-
-var safeOutputsEnvLog = logger.New("workflow:safe_outputs_env")
-
-// ========================================
-// Safe Output Environment Variables
-// ========================================
-
-// applySafeOutputEnvToMap adds safe-output related environment variables to an env map
-// This extracts the duplicated safe-output env setup logic across all engines (copilot, codex, claude, custom)
-func applySafeOutputEnvToMap(env map[string]string, data *WorkflowData) {
- if data.SafeOutputs == nil {
- return
- }
-
- safeOutputsEnvLog.Printf("Applying safe output env vars: trial_mode=%t, staged=%t", data.TrialMode, data.SafeOutputs.Staged)
-
- env["GH_AW_SAFE_OUTPUTS"] = "${{ env.GH_AW_SAFE_OUTPUTS }}"
-
- // Add staged flag if specified
- if data.TrialMode || data.SafeOutputs.Staged {
- env["GH_AW_SAFE_OUTPUTS_STAGED"] = "true"
- }
- if data.TrialMode && data.TrialLogicalRepo != "" {
- env["GH_AW_TARGET_REPO_SLUG"] = data.TrialLogicalRepo
- }
-
- // Add branch name if upload assets is configured
- if data.SafeOutputs.UploadAssets != nil {
- safeOutputsEnvLog.Printf("Adding upload assets env vars: branch=%s", data.SafeOutputs.UploadAssets.BranchName)
- env["GH_AW_ASSETS_BRANCH"] = fmt.Sprintf("%q", data.SafeOutputs.UploadAssets.BranchName)
- env["GH_AW_ASSETS_MAX_SIZE_KB"] = strconv.Itoa(data.SafeOutputs.UploadAssets.MaxSizeKB)
- env["GH_AW_ASSETS_ALLOWED_EXTS"] = fmt.Sprintf("%q", strings.Join(data.SafeOutputs.UploadAssets.AllowedExts, ","))
- }
-}
-
-// buildWorkflowMetadataEnvVars builds workflow name and source environment variables
-// This extracts the duplicated workflow metadata setup logic from safe-output job builders
-func buildWorkflowMetadataEnvVars(workflowName string, workflowSource string) []string {
- var customEnvVars []string
-
- // Add workflow name
- customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_WORKFLOW_NAME: %q\n", workflowName))
-
- // Add workflow source and source URL if present
- if workflowSource != "" {
- customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_WORKFLOW_SOURCE: %q\n", workflowSource))
- sourceURL := buildSourceURL(workflowSource)
- if sourceURL != "" {
- customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_WORKFLOW_SOURCE_URL: %q\n", sourceURL))
- }
- }
-
- return customEnvVars
-}
-
-// buildWorkflowMetadataEnvVarsWithTrackerID builds workflow metadata env vars including tracker-id
-func buildWorkflowMetadataEnvVarsWithTrackerID(workflowName string, workflowSource string, trackerID string) []string {
- customEnvVars := buildWorkflowMetadataEnvVars(workflowName, workflowSource)
-
- // Add tracker-id if present
- if trackerID != "" {
- customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_TRACKER_ID: %q\n", trackerID))
- }
-
- return customEnvVars
-}
-
-// buildSafeOutputJobEnvVars builds environment variables for safe-output jobs with staged/target repo handling
-// This extracts the duplicated env setup logic in safe-output job builders (create_issue, add_comment, etc.)
-func buildSafeOutputJobEnvVars(trialMode bool, trialLogicalRepoSlug string, staged bool, targetRepoSlug string) []string {
- var customEnvVars []string
-
- // Pass the staged flag if it's set to true
- if trialMode || staged {
- safeOutputsEnvLog.Printf("Setting staged flag: trial_mode=%t, staged=%t", trialMode, staged)
- customEnvVars = append(customEnvVars, " GH_AW_SAFE_OUTPUTS_STAGED: \"true\"\n")
- }
-
- // Set GH_AW_TARGET_REPO_SLUG - prefer target-repo config over trial target repo
- if targetRepoSlug != "" {
- safeOutputsEnvLog.Printf("Setting target repo slug from config: %s", targetRepoSlug)
- customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_TARGET_REPO_SLUG: %q\n", targetRepoSlug))
- } else if trialMode && trialLogicalRepoSlug != "" {
- safeOutputsEnvLog.Printf("Setting target repo slug from trial mode: %s", trialLogicalRepoSlug)
- customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_TARGET_REPO_SLUG: %q\n", trialLogicalRepoSlug))
- }
-
- return customEnvVars
-}
-
-// buildStandardSafeOutputEnvVars builds the standard set of environment variables
-// that all safe-output job builders need: metadata + staged/target repo handling
-// This reduces duplication in safe-output job builders
-func (c *Compiler) buildStandardSafeOutputEnvVars(data *WorkflowData, targetRepoSlug string) []string {
- var customEnvVars []string
-
- // Add workflow metadata (name, source, and tracker-id)
- customEnvVars = append(customEnvVars, buildWorkflowMetadataEnvVarsWithTrackerID(data.Name, data.Source, data.TrackerID)...)
-
- // Add engine metadata (id, version, model) for XML comment marker
- customEnvVars = append(customEnvVars, buildEngineMetadataEnvVars(data.EngineConfig)...)
-
- // Add common safe output job environment variables (staged/target repo)
- customEnvVars = append(customEnvVars, buildSafeOutputJobEnvVars(
- c.trialMode,
- c.trialLogicalRepoSlug,
- data.SafeOutputs.Staged,
- targetRepoSlug,
- )...)
-
- // Add messages config if present
- if data.SafeOutputs.Messages != nil {
- messagesJSON, err := serializeMessagesConfig(data.SafeOutputs.Messages)
- if err != nil {
- safeOutputsEnvLog.Printf("Warning: failed to serialize messages config: %v", err)
- } else if messagesJSON != "" {
- customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_SAFE_OUTPUT_MESSAGES: %q\n", messagesJSON))
- }
- }
-
- return customEnvVars
-}
-
-// buildStepLevelSafeOutputEnvVars builds environment variables for consolidated safe output steps
-// This excludes variables that are already set at the job level in consolidated jobs
-func (c *Compiler) buildStepLevelSafeOutputEnvVars(data *WorkflowData, targetRepoSlug string) []string {
- var customEnvVars []string
-
- // Only add target repo slug if it's different from the job-level setting
- // (i.e., this step has a specific target-repo config that overrides the global trial mode target)
- if targetRepoSlug != "" {
- // Step-specific target repo overrides job-level setting
- customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_TARGET_REPO_SLUG: %q\n", targetRepoSlug))
- } else if !c.trialMode && data.SafeOutputs.Staged {
- // Step needs staged flag but there's no job-level target repo (not in trial mode)
- // Job level only sets this if trialMode is true
- customEnvVars = append(customEnvVars, " GH_AW_SAFE_OUTPUTS_STAGED: \"true\"\n")
- }
-
- // Note: The following are now set at job level and should NOT be included here:
- // - GH_AW_WORKFLOW_NAME
- // - GH_AW_WORKFLOW_SOURCE
- // - GH_AW_WORKFLOW_SOURCE_URL
- // - GH_AW_TRACKER_ID
- // - GH_AW_ENGINE_ID
- // - GH_AW_ENGINE_VERSION
- // - GH_AW_ENGINE_MODEL
- // - GH_AW_SAFE_OUTPUTS_STAGED (if in trial mode)
- // - GH_AW_TARGET_REPO_SLUG (if in trial mode and no step override)
- // - GH_AW_SAFE_OUTPUT_MESSAGES
-
- return customEnvVars
-}
-
-// buildEngineMetadataEnvVars builds engine metadata environment variables (id, version, model)
-// These are used by the JavaScript footer generation to create XML comment markers for traceability
-func buildEngineMetadataEnvVars(engineConfig *EngineConfig) []string {
- var customEnvVars []string
-
- if engineConfig == nil {
- return customEnvVars
- }
-
- safeOutputsEnvLog.Printf("Building engine metadata env vars: id=%s, version=%s", engineConfig.ID, engineConfig.Version)
-
- // Add engine ID if present
- if engineConfig.ID != "" {
- customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_ENGINE_ID: %q\n", engineConfig.ID))
- }
-
- // Add engine version if present
- if engineConfig.Version != "" {
- customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_ENGINE_VERSION: %q\n", engineConfig.Version))
- }
-
- // Add engine model if present
- if engineConfig.Model != "" {
- customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_ENGINE_MODEL: %q\n", engineConfig.Model))
- }
-
- return customEnvVars
-}
-
-// ========================================
-// Safe Output Environment Helpers
-// ========================================
-
-// addCustomSafeOutputEnvVars adds custom environment variables to safe output job steps
-func (c *Compiler) addCustomSafeOutputEnvVars(steps *[]string, data *WorkflowData) {
- if data.SafeOutputs != nil && len(data.SafeOutputs.Env) > 0 {
- for key, value := range data.SafeOutputs.Env {
- *steps = append(*steps, fmt.Sprintf(" %s: %s\n", key, value))
- }
- }
-}
-
-// addSafeOutputGitHubTokenForConfig adds github-token to the with section, preferring per-config token over global
-// Uses precedence: config token > safe-outputs global github-token > GH_AW_GITHUB_TOKEN || GITHUB_TOKEN
-func (c *Compiler) addSafeOutputGitHubTokenForConfig(steps *[]string, data *WorkflowData, configToken string) {
- var safeOutputsToken string
- if data.SafeOutputs != nil {
- safeOutputsToken = data.SafeOutputs.GitHubToken
- }
-
- // If app is configured, use app token
- if data.SafeOutputs != nil && data.SafeOutputs.GitHubApp != nil {
- *steps = append(*steps, " github-token: ${{ steps.safe-outputs-app-token.outputs.token }}\n")
- return
- }
-
- // Choose the first non-empty custom token for precedence
- effectiveCustomToken := configToken
- if effectiveCustomToken == "" {
- effectiveCustomToken = safeOutputsToken
- }
-
- // Get effective token
- effectiveToken := getEffectiveSafeOutputGitHubToken(effectiveCustomToken)
- *steps = append(*steps, fmt.Sprintf(" github-token: %s\n", effectiveToken))
-}
-
-// addSafeOutputCopilotGitHubTokenForConfig adds github-token to the with section for Copilot-related operations
-// Uses precedence: config token > safe-outputs global github-token > COPILOT_GITHUB_TOKEN
-func (c *Compiler) addSafeOutputCopilotGitHubTokenForConfig(steps *[]string, data *WorkflowData, configToken string) {
- var safeOutputsToken string
- if data.SafeOutputs != nil {
- safeOutputsToken = data.SafeOutputs.GitHubToken
- }
-
- // If app is configured, use app token
- if data.SafeOutputs != nil && data.SafeOutputs.GitHubApp != nil {
- *steps = append(*steps, " github-token: ${{ steps.safe-outputs-app-token.outputs.token }}\n")
- return
- }
-
- // Choose the first non-empty custom token for precedence
- effectiveCustomToken := configToken
- if effectiveCustomToken == "" {
- effectiveCustomToken = safeOutputsToken
- }
-
- // Get effective token
- effectiveToken := getEffectiveCopilotRequestsToken(effectiveCustomToken)
- *steps = append(*steps, fmt.Sprintf(" github-token: %s\n", effectiveToken))
-}
-
-// addSafeOutputAgentGitHubTokenForConfig adds github-token to the with section for agent assignment operations
-// Uses precedence: config token > safe-outputs token > GH_AW_AGENT_TOKEN || GH_AW_GITHUB_TOKEN || GITHUB_TOKEN
-// This is specifically for assign-to-agent operations which require elevated permissions.
-//
-// Note: GitHub App tokens are intentionally NOT used here, even when github-app: is configured.
-// The Copilot assignment API only accepts PATs (fine-grained or classic), not GitHub App
-// installation tokens. Callers must provide an explicit github-token or rely on GH_AW_AGENT_TOKEN.
-func (c *Compiler) addSafeOutputAgentGitHubTokenForConfig(steps *[]string, data *WorkflowData, configToken string) {
- // Get safe-outputs level token
- var safeOutputsToken string
- if data.SafeOutputs != nil {
- safeOutputsToken = data.SafeOutputs.GitHubToken
- }
-
- // Choose the first non-empty custom token for precedence
- effectiveCustomToken := configToken
- if effectiveCustomToken == "" {
- effectiveCustomToken = safeOutputsToken
- }
-
- // Get effective token - falls back to ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- // when no explicit token is provided. GitHub App tokens are never used here because the
- // Copilot assignment API rejects them.
- effectiveToken := getEffectiveCopilotCodingAgentGitHubToken(effectiveCustomToken)
- *steps = append(*steps, fmt.Sprintf(" github-token: %s\n", effectiveToken))
-}
-
-// buildAllowedReposEnvVar builds an allowed-repos environment variable line for safe-output jobs.
-// envVarName should be the full env var name like "GH_AW_ALLOWED_REPOS".
-// Returns an empty slice if allowedRepos is empty.
-func buildAllowedReposEnvVar(envVarName string, allowedRepos []string) []string {
- if len(allowedRepos) == 0 {
- return nil
- }
- reposStr := strings.Join(allowedRepos, ",")
- return []string{fmt.Sprintf(" %s: %q\n", envVarName, reposStr)}
-}
diff --git a/pkg/workflow/safe_outputs_mcp_integration_test.go b/pkg/workflow/safe_outputs_mcp_integration_test.go
index 2a68100a14f..5c5e5196903 100644
--- a/pkg/workflow/safe_outputs_mcp_integration_test.go
+++ b/pkg/workflow/safe_outputs_mcp_integration_test.go
@@ -53,7 +53,7 @@ Test safe outputs workflow with MCP server integration.
// So we don't check for cat command anymore, we just check the MCP config references it
// Check that safe-outputs configuration file is written
- if !strings.Contains(yamlStr, "cat > /opt/gh-aw/safeoutputs/config.json") {
+ if !strings.Contains(yamlStr, "cat > ${GH_AW_HOME}/safeoutputs/config.json") {
t.Error("Expected safe-outputs configuration to be written to config.json file")
}
@@ -73,7 +73,7 @@ Test safe outputs workflow with MCP server integration.
}
// Check that config file is created
- if !strings.Contains(yamlStr, "cat > /opt/gh-aw/safeoutputs/config.json") {
+ if !strings.Contains(yamlStr, "cat > ${GH_AW_HOME}/safeoutputs/config.json") {
t.Error("Expected config file to be created")
}
@@ -118,7 +118,7 @@ Test workflow without safe outputs.
// The check is now redundant since we removed the cat command entirely
// Check that safe-outputs configuration file is NOT written
- if strings.Contains(yamlStr, "cat > /opt/gh-aw/safeoutputs/config.json") {
+ if strings.Contains(yamlStr, "cat > ${GH_AW_HOME}/safeoutputs/config.json") {
t.Error("Expected safe-outputs configuration to NOT be written when safe-outputs are disabled")
}
@@ -171,7 +171,7 @@ Test safe outputs workflow with Codex engine.
// So we don't check for cat command anymore
// Check that safe-outputs configuration file is written
- if !strings.Contains(yamlStr, "cat > /opt/gh-aw/safeoutputs/config.json") {
+ if !strings.Contains(yamlStr, "cat > ${GH_AW_HOME}/safeoutputs/config.json") {
t.Error("Expected safe-outputs configuration to be written to config.json file")
}
diff --git a/pkg/workflow/safe_output_parser.go b/pkg/workflow/safe_outputs_parser.go
similarity index 98%
rename from pkg/workflow/safe_output_parser.go
rename to pkg/workflow/safe_outputs_parser.go
index 941e186f77c..3bc78d00169 100644
--- a/pkg/workflow/safe_output_parser.go
+++ b/pkg/workflow/safe_outputs_parser.go
@@ -2,7 +2,7 @@ package workflow
import "github.com/github/gh-aw/pkg/logger"
-var safeOutputParserLog = logger.New("workflow:safe_output_parser")
+var safeOutputParserLog = logger.New("workflow:safe_outputs_parser")
// SafeOutputTargetConfig contains common target-related fields for safe output configurations.
// Embed this in safe output config structs that support targeting specific items.
diff --git a/pkg/workflow/safe_outputs_state.go b/pkg/workflow/safe_outputs_state.go
index b605a5068b6..c8a8ce387cf 100644
--- a/pkg/workflow/safe_outputs_state.go
+++ b/pkg/workflow/safe_outputs_state.go
@@ -55,6 +55,7 @@ var safeOutputFieldMapping = map[string]string{
"LinkSubIssue": "link_sub_issue",
"HideComment": "hide_comment",
"DispatchWorkflow": "dispatch_workflow",
+ "CallWorkflow": "call_workflow",
"MissingTool": "missing_tool",
"MissingData": "missing_data",
"SetIssueType": "set_issue_type",
diff --git a/pkg/workflow/safe_outputs_steps.go b/pkg/workflow/safe_outputs_steps.go
new file mode 100644
index 00000000000..ae52bedd6ef
--- /dev/null
+++ b/pkg/workflow/safe_outputs_steps.go
@@ -0,0 +1,263 @@
+package workflow
+
+import (
+ "fmt"
+
+ "github.com/github/gh-aw/pkg/constants"
+ "github.com/github/gh-aw/pkg/logger"
+)
+
+var safeOutputsStepsLog = logger.New("workflow:safe_outputs_steps")
+
+// ========================================
+// Safe Output Step Builders
+// ========================================
+
+// buildCustomActionStep creates a step that uses a custom action reference
+// instead of inline JavaScript via actions/github-script
+func (c *Compiler) buildCustomActionStep(data *WorkflowData, config GitHubScriptStepConfig, scriptName string) []string {
+ safeOutputsStepsLog.Printf("Building custom action step: %s (scriptName=%s, actionMode=%s)", config.StepName, scriptName, c.actionMode)
+
+ var steps []string
+
+ // Get the action path from the script registry
+ actionPath := DefaultScriptRegistry.GetActionPath(scriptName)
+ if actionPath == "" {
+ safeOutputsStepsLog.Printf("WARNING: No action path found for script %s, falling back to inline mode", scriptName)
+ // Set ScriptFile for inline mode fallback
+ config.ScriptFile = scriptName + ".cjs"
+ return c.buildGitHubScriptStep(data, config)
+ }
+
+ // Resolve the action reference based on mode
+ actionRef := c.resolveActionReference(actionPath, data)
+ if actionRef == "" {
+ safeOutputsStepsLog.Printf("WARNING: Could not resolve action reference for %s, falling back to inline mode", actionPath)
+ // Set ScriptFile for inline mode fallback
+ config.ScriptFile = scriptName + ".cjs"
+ return c.buildGitHubScriptStep(data, config)
+ }
+
+ // Add artifact download steps before the custom action step.
+ // In workflow_call context, use the per-invocation prefix to avoid artifact name clashes.
+ // These steps are used in jobs that depend on the agent job (not activation), so use
+ // the agent-downstream prefix expression.
+ steps = append(steps, buildAgentOutputDownloadSteps(artifactPrefixExprForAgentDownstreamJob(data))...)
+
+ // Step name and metadata
+ steps = append(steps, fmt.Sprintf(" - name: %s\n", config.StepName))
+ steps = append(steps, fmt.Sprintf(" id: %s\n", config.StepID))
+ steps = append(steps, fmt.Sprintf(" uses: %s\n", actionRef))
+
+ // Environment variables section
+ steps = append(steps, " env:\n")
+ steps = append(steps, " GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}\n")
+ steps = append(steps, config.CustomEnvVars...)
+ c.addCustomSafeOutputEnvVars(&steps, data)
+
+ // With section for inputs (replaces github-token in actions/github-script)
+ steps = append(steps, " with:\n")
+
+ // Map github-token to token input for custom actions
+ c.addCustomActionGitHubToken(&steps, data, config)
+
+ return steps
+}
+
+// addCustomActionGitHubToken adds a GitHub token as action input.
+// The token precedence depends on the tokenType flags:
+// - UseCopilotCodingAgentToken: customToken > SafeOutputs.GitHubToken > GH_AW_AGENT_TOKEN || GH_AW_GITHUB_TOKEN || GITHUB_TOKEN
+// - UseCopilotRequestsToken: customToken > SafeOutputs.GitHubToken > COPILOT_GITHUB_TOKEN
+// - Default: customToken > SafeOutputs.GitHubToken > GH_AW_GITHUB_TOKEN || GITHUB_TOKEN
+func (c *Compiler) addCustomActionGitHubToken(steps *[]string, data *WorkflowData, config GitHubScriptStepConfig) {
+ safeOutputsStepsLog.Printf("Selecting GitHub token for step: %s (copilotCodingAgent=%t, copilotRequests=%t)", config.StepName, config.UseCopilotCodingAgentToken, config.UseCopilotRequestsToken)
+ var token string
+
+ // Get safe-outputs level token
+ var safeOutputsToken string
+ if data.SafeOutputs != nil {
+ safeOutputsToken = data.SafeOutputs.GitHubToken
+ }
+
+ // Choose the first non-empty custom token for precedence
+ effectiveCustomToken := config.CustomToken
+ if effectiveCustomToken == "" {
+ effectiveCustomToken = safeOutputsToken
+ }
+
+ // Agent token mode: use full precedence chain for agent assignment
+ if config.UseCopilotCodingAgentToken {
+ token = getEffectiveCopilotCodingAgentGitHubToken(effectiveCustomToken)
+ } else if config.UseCopilotRequestsToken {
+ // Copilot mode: use getEffectiveCopilotRequestsToken with safe-outputs token precedence
+ token = getEffectiveCopilotRequestsToken(effectiveCustomToken)
+ } else {
+ // Standard mode: use safe output token chain
+ token = getEffectiveSafeOutputGitHubToken(effectiveCustomToken)
+ }
+
+ *steps = append(*steps, fmt.Sprintf(" token: %s\n", token))
+}
+
+// GitHubScriptStepConfig holds configuration for building a GitHub Script step
+type GitHubScriptStepConfig struct {
+ // Step metadata
+ StepName string // e.g., "Create Output Issue"
+ StepID string // e.g., "create_issue"
+
+ // Main job reference for agent output
+ MainJobName string
+
+ // Environment variables specific to this safe output type
+ // These are added after GH_AW_AGENT_OUTPUT
+ CustomEnvVars []string
+
+ // JavaScript script constant to format and include (for inline mode)
+ Script string
+
+ // ScriptFile is the .cjs filename to require (e.g., "noop.cjs")
+ // If empty, Script will be inlined instead
+ ScriptFile string
+
+ // CustomToken configuration (passed to addSafeOutputGitHubTokenForConfig or addSafeOutputCopilotGitHubTokenForConfig)
+ CustomToken string
+
+ // UseCopilotRequestsToken indicates whether to use the Copilot token preference chain
+ // custom token > COPILOT_GITHUB_TOKEN
+ // This should be true for Copilot-related operations like creating agent tasks,
+ // assigning copilot to issues, or adding copilot as PR reviewer
+ UseCopilotRequestsToken bool
+
+ // UseCopilotCodingAgentToken indicates whether to use the agent token preference chain
+ // (config token > GH_AW_AGENT_TOKEN)
+ // This should be true for agent assignment operations (assign-to-agent)
+ UseCopilotCodingAgentToken bool
+}
+
+// buildGitHubScriptStep creates a GitHub Script step with common scaffolding
+// This extracts the repeated pattern found across safe output job builders
+func (c *Compiler) buildGitHubScriptStep(data *WorkflowData, config GitHubScriptStepConfig) []string {
+ safeOutputsStepsLog.Printf("Building GitHub Script step: %s (useCopilotRequestsToken=%v, useCopilotCodingAgentToken=%v)", config.StepName, config.UseCopilotRequestsToken, config.UseCopilotCodingAgentToken)
+
+ var steps []string
+
+ // Add artifact download steps before the GitHub Script step.
+ // In workflow_call context, use the per-invocation prefix to avoid artifact name clashes.
+ // These steps are used in jobs that depend on the agent job (not activation), so use
+ // the agent-downstream prefix expression.
+ steps = append(steps, buildAgentOutputDownloadSteps(artifactPrefixExprForAgentDownstreamJob(data))...)
+
+ // Step name and metadata
+ steps = append(steps, fmt.Sprintf(" - name: %s\n", config.StepName))
+ steps = append(steps, fmt.Sprintf(" id: %s\n", config.StepID))
+ steps = append(steps, fmt.Sprintf(" uses: %s\n", GetActionPin("actions/github-script")))
+
+ // Environment variables section
+ steps = append(steps, " env:\n")
+
+ // Read GH_AW_AGENT_OUTPUT from environment (set by artifact download step)
+ // instead of directly from job outputs which may be masked by GitHub Actions
+ steps = append(steps, " GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}\n")
+
+ // Add custom environment variables specific to this safe output type
+ steps = append(steps, config.CustomEnvVars...)
+
+ // Add custom environment variables from safe-outputs.env
+ c.addCustomSafeOutputEnvVars(&steps, data)
+
+ // With section for github-token
+ steps = append(steps, " with:\n")
+ if config.UseCopilotCodingAgentToken {
+ c.addSafeOutputAgentGitHubTokenForConfig(&steps, data, config.CustomToken)
+ } else if config.UseCopilotRequestsToken {
+ c.addSafeOutputCopilotGitHubTokenForConfig(&steps, data, config.CustomToken)
+ } else {
+ c.addSafeOutputGitHubTokenForConfig(&steps, data, config.CustomToken)
+ }
+
+ steps = append(steps, " script: |\n")
+
+ // Use require() if ScriptFile is specified, otherwise inline the script
+ if config.ScriptFile != "" {
+ steps = append(steps, " const { setupGlobals } = require("+JsRequireGhAw("actions/setup_globals.cjs")+");\n")
+ steps = append(steps, " setupGlobals(core, github, context, exec, io);\n")
+ steps = append(steps, fmt.Sprintf(" const { main } = require(%s);\n", JsRequireGhAw("actions/"+config.ScriptFile)))
+ steps = append(steps, " await main();\n")
+ } else {
+ // Add the formatted JavaScript script (inline)
+ formattedScript := FormatJavaScriptForYAML(config.Script)
+ steps = append(steps, formattedScript...)
+ }
+
+ return steps
+}
+
+// buildGitHubScriptStepWithoutDownload creates a GitHub Script step without artifact download steps
+// This is useful when multiple script steps are needed in the same job and artifact downloads
+// should only happen once at the beginning
+func (c *Compiler) buildGitHubScriptStepWithoutDownload(data *WorkflowData, config GitHubScriptStepConfig) []string {
+ safeOutputsStepsLog.Printf("Building GitHub Script step without download: %s", config.StepName)
+
+ var steps []string
+
+ // Step name and metadata (no artifact download steps)
+ steps = append(steps, fmt.Sprintf(" - name: %s\n", config.StepName))
+ steps = append(steps, fmt.Sprintf(" id: %s\n", config.StepID))
+ steps = append(steps, fmt.Sprintf(" uses: %s\n", GetActionPin("actions/github-script")))
+
+ // Environment variables section
+ steps = append(steps, " env:\n")
+
+ // Read GH_AW_AGENT_OUTPUT from environment (set by artifact download step)
+ // instead of directly from job outputs which may be masked by GitHub Actions
+ steps = append(steps, " GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}\n")
+
+ // Add custom environment variables specific to this safe output type
+ steps = append(steps, config.CustomEnvVars...)
+
+ // Add custom environment variables from safe-outputs.env
+ c.addCustomSafeOutputEnvVars(&steps, data)
+
+ // With section for github-token
+ steps = append(steps, " with:\n")
+ if config.UseCopilotCodingAgentToken {
+ c.addSafeOutputAgentGitHubTokenForConfig(&steps, data, config.CustomToken)
+ } else if config.UseCopilotRequestsToken {
+ c.addSafeOutputCopilotGitHubTokenForConfig(&steps, data, config.CustomToken)
+ } else {
+ c.addSafeOutputGitHubTokenForConfig(&steps, data, config.CustomToken)
+ }
+
+ steps = append(steps, " script: |\n")
+
+ // Use require() if ScriptFile is specified, otherwise inline the script
+ if config.ScriptFile != "" {
+ steps = append(steps, " const { setupGlobals } = require("+JsRequireGhAw("actions/setup_globals.cjs")+");\n")
+ steps = append(steps, " setupGlobals(core, github, context, exec, io);\n")
+ steps = append(steps, fmt.Sprintf(" const { main } = require(%s);\n", JsRequireGhAw("actions/"+config.ScriptFile)))
+ steps = append(steps, " await main();\n")
+ } else {
+ // Add the formatted JavaScript script (inline)
+ formattedScript := FormatJavaScriptForYAML(config.Script)
+ steps = append(steps, formattedScript...)
+ }
+
+ return steps
+}
+
+// buildAgentOutputDownloadSteps creates steps to download the agent output artifact
+// and set the GH_AW_AGENT_OUTPUT environment variable for safe-output jobs.
+// GH_AW_AGENT_OUTPUT is only set when the artifact was actually downloaded successfully.
+// prefix is prepended to the artifact name; use empty string for non-workflow_call workflows.
+func buildAgentOutputDownloadSteps(prefix string) []string {
+ safeOutputsStepsLog.Printf("Building agent output download steps with prefix: %q", prefix)
+ return buildArtifactDownloadSteps(ArtifactDownloadConfig{
+ ArtifactName: prefix + constants.AgentArtifactName, // Unified agent artifact (prefixed in workflow_call)
+ ArtifactFilename: constants.AgentOutputFilename, // Filename inside the artifact directory
+ DownloadPath: "/tmp/gh-aw/",
+ SetupEnvStep: true,
+ EnvVarName: "GH_AW_AGENT_OUTPUT",
+ StepName: "Download agent output artifact",
+ StepID: "download-agent-output",
+ })
+}
diff --git a/pkg/workflow/safe_outputs_tools_filtering.go b/pkg/workflow/safe_outputs_tools_filtering.go
index 3cc1f8f2529..0f6245c71c6 100644
--- a/pkg/workflow/safe_outputs_tools_filtering.go
+++ b/pkg/workflow/safe_outputs_tools_filtering.go
@@ -18,6 +18,20 @@ import (
// filtered subset containing only those tools enabled by the workflow's
// SafeOutputsConfig. Dynamic tools (dispatch-workflow, custom jobs) are also
// generated here.
+//
+// There are two generation strategies:
+//
+// 1. generateFilteredToolsJSON: Legacy approach that loads the embedded
+// safe_outputs_tools.json, filters and enhances it in Go, then returns the
+// full JSON to be inlined directly into the compiled workflow YAML as a
+// heredoc. Used by tests and kept for backward compatibility.
+//
+// 2. generateToolsMetaJSON: New approach that generates a small "meta" JSON
+// (description suffixes, repo params, dynamic tools) to be written at
+// compile time. At runtime, generate_safe_outputs_tools.cjs reads the
+// source safe_outputs_tools.json from the actions folder, applies the meta
+// overrides, and writes the final tools.json—avoiding inlining the entire
+// file into the compiled workflow YAML.
// generateFilteredToolsJSON filters the ALL_TOOLS array based on enabled safe outputs.
// Returns a JSON string containing only the tools that are enabled in the workflow.
@@ -529,3 +543,383 @@ func addRepoParameterIfNeeded(tool map[string]any, toolName string, safeOutputs
safeOutputsConfigLog.Printf("Added repo parameter to tool: %s (has allowed-repos or wildcard target-repo)", toolName)
}
+
+// computeEnabledToolNames returns the set of predefined tool names that are enabled
+// by the workflow's SafeOutputsConfig. Dynamic tools (dispatch-workflow, custom jobs,
+// call-workflow) are excluded because they are generated separately.
+func computeEnabledToolNames(data *WorkflowData) map[string]bool {
+ enabledTools := make(map[string]bool)
+ if data.SafeOutputs == nil {
+ return enabledTools
+ }
+
+ if data.SafeOutputs.CreateIssues != nil {
+ enabledTools["create_issue"] = true
+ }
+ if data.SafeOutputs.CreateAgentSessions != nil {
+ enabledTools["create_agent_session"] = true
+ }
+ if data.SafeOutputs.CreateDiscussions != nil {
+ enabledTools["create_discussion"] = true
+ }
+ if data.SafeOutputs.UpdateDiscussions != nil {
+ enabledTools["update_discussion"] = true
+ }
+ if data.SafeOutputs.CloseDiscussions != nil {
+ enabledTools["close_discussion"] = true
+ }
+ if data.SafeOutputs.CloseIssues != nil {
+ enabledTools["close_issue"] = true
+ }
+ if data.SafeOutputs.ClosePullRequests != nil {
+ enabledTools["close_pull_request"] = true
+ }
+ if data.SafeOutputs.MarkPullRequestAsReadyForReview != nil {
+ enabledTools["mark_pull_request_as_ready_for_review"] = true
+ }
+ if data.SafeOutputs.AddComments != nil {
+ enabledTools["add_comment"] = true
+ }
+ if data.SafeOutputs.CreatePullRequests != nil {
+ enabledTools["create_pull_request"] = true
+ }
+ if data.SafeOutputs.CreatePullRequestReviewComments != nil {
+ enabledTools["create_pull_request_review_comment"] = true
+ }
+ if data.SafeOutputs.SubmitPullRequestReview != nil {
+ enabledTools["submit_pull_request_review"] = true
+ }
+ if data.SafeOutputs.ReplyToPullRequestReviewComment != nil {
+ enabledTools["reply_to_pull_request_review_comment"] = true
+ }
+ if data.SafeOutputs.ResolvePullRequestReviewThread != nil {
+ enabledTools["resolve_pull_request_review_thread"] = true
+ }
+ if data.SafeOutputs.CreateCodeScanningAlerts != nil {
+ enabledTools["create_code_scanning_alert"] = true
+ }
+ if data.SafeOutputs.AutofixCodeScanningAlert != nil {
+ enabledTools["autofix_code_scanning_alert"] = true
+ }
+ if data.SafeOutputs.AddLabels != nil {
+ enabledTools["add_labels"] = true
+ }
+ if data.SafeOutputs.RemoveLabels != nil {
+ enabledTools["remove_labels"] = true
+ }
+ if data.SafeOutputs.AddReviewer != nil {
+ enabledTools["add_reviewer"] = true
+ }
+ if data.SafeOutputs.AssignMilestone != nil {
+ enabledTools["assign_milestone"] = true
+ }
+ if data.SafeOutputs.AssignToAgent != nil {
+ enabledTools["assign_to_agent"] = true
+ }
+ if data.SafeOutputs.AssignToUser != nil {
+ enabledTools["assign_to_user"] = true
+ }
+ if data.SafeOutputs.UnassignFromUser != nil {
+ enabledTools["unassign_from_user"] = true
+ }
+ if data.SafeOutputs.UpdateIssues != nil {
+ enabledTools["update_issue"] = true
+ }
+ if data.SafeOutputs.UpdatePullRequests != nil {
+ enabledTools["update_pull_request"] = true
+ }
+ if data.SafeOutputs.PushToPullRequestBranch != nil {
+ enabledTools["push_to_pull_request_branch"] = true
+ }
+ if data.SafeOutputs.UploadAssets != nil {
+ enabledTools["upload_asset"] = true
+ }
+ if data.SafeOutputs.MissingTool != nil {
+ enabledTools["missing_tool"] = true
+ }
+ if data.SafeOutputs.MissingData != nil {
+ enabledTools["missing_data"] = true
+ }
+ if data.SafeOutputs.UpdateRelease != nil {
+ enabledTools["update_release"] = true
+ }
+ if data.SafeOutputs.NoOp != nil {
+ enabledTools["noop"] = true
+ }
+ if data.SafeOutputs.LinkSubIssue != nil {
+ enabledTools["link_sub_issue"] = true
+ }
+ if data.SafeOutputs.HideComment != nil {
+ enabledTools["hide_comment"] = true
+ }
+ if data.SafeOutputs.SetIssueType != nil {
+ enabledTools["set_issue_type"] = true
+ }
+ if data.SafeOutputs.UpdateProjects != nil {
+ enabledTools["update_project"] = true
+ }
+ if data.SafeOutputs.CreateProjectStatusUpdates != nil {
+ enabledTools["create_project_status_update"] = true
+ }
+ if data.SafeOutputs.CreateProjects != nil {
+ enabledTools["create_project"] = true
+ }
+
+ // Add push_repo_memory tool if repo-memory is configured
+ if data.RepoMemoryConfig != nil && len(data.RepoMemoryConfig.Memories) > 0 {
+ enabledTools["push_repo_memory"] = true
+ }
+
+ return enabledTools
+}
+
+// computeRepoParamForTool returns the "repo" input parameter definition that should
+// be added to a tool's inputSchema, or nil if no repo parameter is needed.
+// This mirrors the logic in addRepoParameterIfNeeded but returns the param instead
+// of modifying a tool in place, making it usable for generateToolsMetaJSON.
+func computeRepoParamForTool(toolName string, safeOutputs *SafeOutputsConfig) map[string]any {
+ // Reuse addRepoParameterIfNeeded by passing a scratch tool with an empty inputSchema.
+ scratch := map[string]any{
+ "name": toolName,
+ "inputSchema": map[string]any{"properties": map[string]any{}},
+ }
+ addRepoParameterIfNeeded(scratch, toolName, safeOutputs)
+
+ inputSchema, ok := scratch["inputSchema"].(map[string]any)
+ if !ok {
+ return nil
+ }
+ properties, ok := inputSchema["properties"].(map[string]any)
+ if !ok {
+ return nil
+ }
+ repoProp, ok := properties["repo"].(map[string]any)
+ if !ok {
+ return nil
+ }
+ return repoProp
+}
+
+// generateDynamicTools generates MCP tool definitions for dynamic tools:
+// custom safe-jobs, dispatch_workflow targets, and call_workflow targets.
+// These tools are not in safe_outputs_tools.json and must be generated from
+// the workflow configuration at compile time.
+func generateDynamicTools(data *WorkflowData, markdownPath string) ([]map[string]any, error) {
+ var dynamicTools []map[string]any
+
+ // Add custom job tools from SafeOutputs.Jobs
+ if len(data.SafeOutputs.Jobs) > 0 {
+ safeOutputsConfigLog.Printf("Adding %d custom job tools", len(data.SafeOutputs.Jobs))
+
+ // Sort job names for deterministic output
+ jobNames := make([]string, 0, len(data.SafeOutputs.Jobs))
+ for jobName := range data.SafeOutputs.Jobs {
+ jobNames = append(jobNames, jobName)
+ }
+ sort.Strings(jobNames)
+
+ for _, jobName := range jobNames {
+ jobConfig := data.SafeOutputs.Jobs[jobName]
+ normalizedJobName := stringutil.NormalizeSafeOutputIdentifier(jobName)
+ customTool := generateCustomJobToolDefinition(normalizedJobName, jobConfig)
+ dynamicTools = append(dynamicTools, customTool)
+ }
+ }
+
+ // Add dynamic dispatch_workflow tools
+ if data.SafeOutputs.DispatchWorkflow != nil && len(data.SafeOutputs.DispatchWorkflow.Workflows) > 0 {
+ safeOutputsConfigLog.Printf("Adding %d dispatch_workflow tools", len(data.SafeOutputs.DispatchWorkflow.Workflows))
+
+ if data.SafeOutputs.DispatchWorkflow.WorkflowFiles == nil {
+ data.SafeOutputs.DispatchWorkflow.WorkflowFiles = make(map[string]string)
+ }
+
+ for _, workflowName := range data.SafeOutputs.DispatchWorkflow.Workflows {
+ fileResult, err := findWorkflowFile(workflowName, markdownPath)
+ if err != nil {
+ safeOutputsConfigLog.Printf("Warning: error finding workflow %s: %v", workflowName, err)
+ dynamicTools = append(dynamicTools, generateDispatchWorkflowTool(workflowName, make(map[string]any)))
+ continue
+ }
+
+ var workflowPath string
+ var extension string
+ var useMD bool
+ if fileResult.lockExists {
+ workflowPath = fileResult.lockPath
+ extension = ".lock.yml"
+ } else if fileResult.ymlExists {
+ workflowPath = fileResult.ymlPath
+ extension = ".yml"
+ } else if fileResult.mdExists {
+ workflowPath = fileResult.mdPath
+ extension = ".lock.yml"
+ useMD = true
+ } else {
+ safeOutputsConfigLog.Printf("Warning: no workflow file found for %s (checked .lock.yml, .yml, .md)", workflowName)
+ dynamicTools = append(dynamicTools, generateDispatchWorkflowTool(workflowName, make(map[string]any)))
+ continue
+ }
+
+ data.SafeOutputs.DispatchWorkflow.WorkflowFiles[workflowName] = extension
+
+ var workflowInputs map[string]any
+ var inputsErr error
+ if useMD {
+ workflowInputs, inputsErr = extractMDWorkflowDispatchInputs(workflowPath)
+ } else {
+ workflowInputs, inputsErr = extractWorkflowDispatchInputs(workflowPath)
+ }
+ if inputsErr != nil {
+ safeOutputsConfigLog.Printf("Warning: failed to extract inputs for workflow %s from %s: %v", workflowName, workflowPath, inputsErr)
+ workflowInputs = make(map[string]any)
+ }
+
+ dynamicTools = append(dynamicTools, generateDispatchWorkflowTool(workflowName, workflowInputs))
+ }
+ }
+
+ // Add dynamic call_workflow tools
+ if data.SafeOutputs.CallWorkflow != nil && len(data.SafeOutputs.CallWorkflow.Workflows) > 0 {
+ safeOutputsConfigLog.Printf("Adding %d call_workflow tools", len(data.SafeOutputs.CallWorkflow.Workflows))
+
+ if data.SafeOutputs.CallWorkflow.WorkflowFiles == nil {
+ data.SafeOutputs.CallWorkflow.WorkflowFiles = make(map[string]string)
+ }
+
+ for _, workflowName := range data.SafeOutputs.CallWorkflow.Workflows {
+ fileResult, err := findWorkflowFile(workflowName, markdownPath)
+ if err != nil {
+ safeOutputsConfigLog.Printf("Warning: error finding workflow %s: %v", workflowName, err)
+ dynamicTools = append(dynamicTools, generateCallWorkflowTool(workflowName, make(map[string]any)))
+ continue
+ }
+
+ var workflowPath string
+ var extension string
+ var useMD bool
+ if fileResult.lockExists {
+ workflowPath = fileResult.lockPath
+ extension = ".lock.yml"
+ } else if fileResult.ymlExists {
+ workflowPath = fileResult.ymlPath
+ extension = ".yml"
+ } else if fileResult.mdExists {
+ workflowPath = fileResult.mdPath
+ extension = ".lock.yml"
+ useMD = true
+ } else {
+ safeOutputsConfigLog.Printf("Warning: no workflow file found for %s (checked .lock.yml, .yml, .md)", workflowName)
+ dynamicTools = append(dynamicTools, generateCallWorkflowTool(workflowName, make(map[string]any)))
+ continue
+ }
+
+ relativePath := fmt.Sprintf("./.github/workflows/%s%s", workflowName, extension)
+ data.SafeOutputs.CallWorkflow.WorkflowFiles[workflowName] = relativePath
+
+ var workflowInputs map[string]any
+ var inputsErr error
+ if useMD {
+ workflowInputs, inputsErr = extractMDWorkflowCallInputs(workflowPath)
+ } else {
+ workflowInputs, inputsErr = extractWorkflowCallInputs(workflowPath)
+ }
+ if inputsErr != nil {
+ safeOutputsConfigLog.Printf("Warning: failed to extract inputs for workflow %s from %s: %v", workflowName, workflowPath, inputsErr)
+ workflowInputs = make(map[string]any)
+ }
+
+ dynamicTools = append(dynamicTools, generateCallWorkflowTool(workflowName, workflowInputs))
+ }
+ }
+
+ return dynamicTools, nil
+}
+
+// ToolsMeta is the structure written to tools_meta.json at compile time and read
+// by generate_safe_outputs_tools.cjs at runtime. It avoids inlining the entire
+// safe_outputs_tools.json into the compiled workflow YAML.
+type ToolsMeta struct {
+ // DescriptionSuffixes maps tool name → constraint text to append to the base description.
+ // Example: " CONSTRAINTS: Maximum 5 issue(s) can be created."
+ DescriptionSuffixes map[string]string `json:"description_suffixes"`
+ // RepoParams maps tool name → "repo" inputSchema property definition, only present
+ // when allowed-repos or a wildcard target-repo is configured for that tool.
+ RepoParams map[string]map[string]any `json:"repo_params"`
+ // DynamicTools contains tool definitions for custom safe-jobs, dispatch_workflow
+ // targets, and call_workflow targets. These are workflow-specific and cannot be
+ // derived from the static safe_outputs_tools.json at runtime.
+ DynamicTools []map[string]any `json:"dynamic_tools"`
+}
+
+// generateToolsMetaJSON generates the content for tools_meta.json: a compact file
+// that captures the workflow-specific customisations (description constraints,
+// repo parameters, dynamic tools) without inlining the entire
+// safe_outputs_tools.json into the compiled workflow YAML.
+//
+// At runtime, generate_safe_outputs_tools.cjs reads safe_outputs_tools.json from
+// the actions folder, applies the meta overrides from tools_meta.json, and writes
+// the final /opt/gh-aw/safeoutputs/tools.json.
+func generateToolsMetaJSON(data *WorkflowData, markdownPath string) (string, error) {
+ if data.SafeOutputs == nil {
+ empty := ToolsMeta{
+ DescriptionSuffixes: map[string]string{},
+ RepoParams: map[string]map[string]any{},
+ DynamicTools: []map[string]any{},
+ }
+ result, err := json.Marshal(empty)
+ if err != nil {
+ return "", fmt.Errorf("failed to marshal empty tools meta: %w", err)
+ }
+ return string(result), nil
+ }
+
+ safeOutputsConfigLog.Print("Generating tools meta JSON for workflow")
+
+ enabledTools := computeEnabledToolNames(data)
+
+ // Compute description suffix for each enabled predefined tool.
+ // enhanceToolDescription with an empty base returns just the constraint text
+ // (e.g. " CONSTRAINTS: Maximum 5 issue(s).") so JavaScript can append it.
+ descriptionSuffixes := make(map[string]string)
+ for toolName := range enabledTools {
+ suffix := enhanceToolDescription(toolName, "", data.SafeOutputs)
+ if suffix != "" {
+ descriptionSuffixes[toolName] = suffix
+ }
+ }
+
+ // Compute repo parameter definition for each tool that needs it.
+ repoParams := make(map[string]map[string]any)
+ for toolName := range enabledTools {
+ if param := computeRepoParamForTool(toolName, data.SafeOutputs); param != nil {
+ repoParams[toolName] = param
+ }
+ }
+
+ // Generate dynamic tool definitions (custom jobs + dispatch/call workflow tools).
+ dynamicTools, err := generateDynamicTools(data, markdownPath)
+ if err != nil {
+ safeOutputsConfigLog.Printf("Error generating dynamic tools: %v", err)
+ dynamicTools = []map[string]any{}
+ }
+ if dynamicTools == nil {
+ dynamicTools = []map[string]any{}
+ }
+
+ meta := ToolsMeta{
+ DescriptionSuffixes: descriptionSuffixes,
+ RepoParams: repoParams,
+ DynamicTools: dynamicTools,
+ }
+
+ result, err := json.MarshalIndent(meta, "", " ")
+ if err != nil {
+ safeOutputsConfigLog.Printf("Failed to marshal tools meta: %v", err)
+ return "", fmt.Errorf("failed to marshal tools meta: %w", err)
+ }
+
+ safeOutputsConfigLog.Printf("Successfully generated tools meta JSON: %d description suffixes, %d repo params, %d dynamic tools",
+ len(descriptionSuffixes), len(repoParams), len(dynamicTools))
+ return string(result), nil
+}
diff --git a/pkg/workflow/safe_outputs_tools_meta_integration_test.go b/pkg/workflow/safe_outputs_tools_meta_integration_test.go
new file mode 100644
index 00000000000..ed016447c4a
--- /dev/null
+++ b/pkg/workflow/safe_outputs_tools_meta_integration_test.go
@@ -0,0 +1,580 @@
+//go:build integration
+
+package workflow
+
+import (
+ "encoding/json"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/github/gh-aw/pkg/testutil"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+// TestToolsMetaJSONContainsDescriptionSuffixes verifies that the compiled workflow YAML
+// embeds a tools_meta.json with the correct description suffixes instead of the large
+// inlined tools.json used in the old approach.
+func TestToolsMetaJSONContainsDescriptionSuffixes(t *testing.T) {
+ tmpDir := testutil.TempDir(t, "tools-meta-desc-test")
+
+ testContent := `---
+on: push
+name: Test Tools Meta Description
+engine: copilot
+safe-outputs:
+ create-issue:
+ max: 5
+ title-prefix: "[bot] "
+ labels:
+ - automation
+ - testing
+ add-comment:
+ max: 10
+---
+
+Test workflow for tools meta description suffixes.
+`
+
+ testFile := filepath.Join(tmpDir, "test-tools-meta-desc.md")
+ require.NoError(t, os.WriteFile(testFile, []byte(testContent), 0644), "should write test file")
+
+ compiler := NewCompiler()
+ require.NoError(t, compiler.CompileWorkflow(testFile), "compilation should succeed")
+
+ lockFile := filepath.Join(tmpDir, "test-tools-meta-desc.lock.yml")
+ yamlBytes, err := os.ReadFile(lockFile)
+ require.NoError(t, err, "should read lock file")
+ yamlStr := string(yamlBytes)
+
+ // Must write tools_meta.json (new approach), NOT tools.json
+ assert.Contains(t, yamlStr, "cat > /opt/gh-aw/safeoutputs/tools_meta.json", "should write tools_meta.json")
+ assert.NotContains(t, yamlStr, "cat > /opt/gh-aw/safeoutputs/tools.json", "should NOT inline tools.json")
+
+ // Must invoke the JavaScript generator at runtime
+ assert.Contains(t, yamlStr, "node /opt/gh-aw/actions/generate_safe_outputs_tools.cjs", "should invoke JS generator")
+
+ // Extract the tools_meta.json content from the lock file
+ meta := extractToolsMetaFromLockFile(t, yamlStr)
+
+ // Verify description suffixes are present and correct
+ createIssueSuffix, ok := meta.DescriptionSuffixes["create_issue"]
+ require.True(t, ok, "create_issue should have a description suffix")
+ assert.Contains(t, createIssueSuffix, "CONSTRAINTS:", "suffix should contain CONSTRAINTS")
+ assert.Contains(t, createIssueSuffix, "Maximum 5 issue(s)", "suffix should include max constraint")
+ assert.Contains(t, createIssueSuffix, `Title will be prefixed with "[bot] "`, "suffix should include title prefix")
+ assert.Contains(t, createIssueSuffix, `Labels ["automation" "testing"]`, "suffix should include labels")
+
+ addCommentSuffix, ok := meta.DescriptionSuffixes["add_comment"]
+ require.True(t, ok, "add_comment should have a description suffix")
+ assert.Contains(t, addCommentSuffix, "Maximum 10 comment(s)", "suffix should include max constraint")
+}
+
+// TestToolsMetaJSONDescriptionsMatchFilteredTools verifies that the description suffixes
+// in tools_meta.json match what generateFilteredToolsJSON would have embedded directly.
+// This is the primary regression test for the strategy change.
+func TestToolsMetaJSONDescriptionsMatchFilteredTools(t *testing.T) {
+ tests := []struct {
+ name string
+ safeOutputs *SafeOutputsConfig
+ toolName string
+ }{
+ {
+ name: "create_issue with max and labels",
+ safeOutputs: &SafeOutputsConfig{
+ CreateIssues: &CreateIssuesConfig{
+ BaseSafeOutputConfig: BaseSafeOutputConfig{Max: strPtr("3")},
+ TitlePrefix: "[automated] ",
+ Labels: []string{"bot", "enhancement"},
+ AllowedLabels: []string{"bug", "feature"},
+ },
+ },
+ toolName: "create_issue",
+ },
+ {
+ name: "add_comment with max and target",
+ safeOutputs: &SafeOutputsConfig{
+ AddComments: &AddCommentsConfig{
+ BaseSafeOutputConfig: BaseSafeOutputConfig{Max: strPtr("5")},
+ Target: "trigger",
+ },
+ },
+ toolName: "add_comment",
+ },
+ {
+ name: "create_pull_request with draft and reviewers",
+ safeOutputs: &SafeOutputsConfig{
+ CreatePullRequests: &CreatePullRequestsConfig{
+ BaseSafeOutputConfig: BaseSafeOutputConfig{Max: strPtr("2")},
+ TitlePrefix: "[pr] ",
+ Labels: []string{"ci"},
+ Draft: strPtr("true"),
+ Reviewers: []string{"reviewer1"},
+ },
+ },
+ toolName: "create_pull_request",
+ },
+ {
+ name: "upload_asset with max and size limit",
+ safeOutputs: &SafeOutputsConfig{
+ UploadAssets: &UploadAssetsConfig{
+ BaseSafeOutputConfig: BaseSafeOutputConfig{Max: strPtr("1")},
+ MaxSizeKB: 1024,
+ AllowedExts: []string{".zip", ".tar.gz"},
+ },
+ },
+ toolName: "upload_asset",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ data := &WorkflowData{SafeOutputs: tt.safeOutputs}
+
+ // Old approach: full tool JSON with description already embedded
+ filteredJSON, err := generateFilteredToolsJSON(data, ".github/workflows/test.md")
+ require.NoError(t, err, "generateFilteredToolsJSON should not error")
+
+ var filteredTools []map[string]any
+ require.NoError(t, json.Unmarshal([]byte(filteredJSON), &filteredTools), "filtered JSON should parse")
+
+ var oldTool map[string]any
+ for _, tool := range filteredTools {
+ if tool["name"] == tt.toolName {
+ oldTool = tool
+ break
+ }
+ }
+ require.NotNil(t, oldTool, "old approach should include tool %s", tt.toolName)
+ oldDescription, ok := oldTool["description"].(string)
+ require.True(t, ok, "old tool should have string description")
+
+ // New approach: tools_meta.json with description suffix
+ metaJSON, err := generateToolsMetaJSON(data, ".github/workflows/test.md")
+ require.NoError(t, err, "generateToolsMetaJSON should not error")
+
+ var meta ToolsMeta
+ require.NoError(t, json.Unmarshal([]byte(metaJSON), &meta), "meta JSON should parse")
+
+ // The suffix should equal the constraint portion of the old description.
+ // Old description = baseDescription + suffix
+ // Example: "Create a new GitHub issue..." + " CONSTRAINTS: Maximum 3 issue(s) can be created."
+ suffix, hasSuffix := meta.DescriptionSuffixes[tt.toolName]
+ if strings.Contains(oldDescription, "CONSTRAINTS:") {
+ require.True(t, hasSuffix, "tools_meta should have suffix for %s when old description has constraints", tt.toolName)
+ assert.True(t, strings.HasSuffix(oldDescription, suffix),
+ "old description should end with the suffix from tools_meta\n old: %q\n suffix: %q", oldDescription, suffix)
+ } else {
+ assert.False(t, hasSuffix, "tools_meta should NOT have suffix for %s when there are no constraints", tt.toolName)
+ }
+ })
+ }
+}
+
+// TestToolsMetaJSONRepoParamsMatchFilteredTools verifies that repo parameter definitions
+// in tools_meta.json match the repo parameters embedded in the old full tools.json.
+func TestToolsMetaJSONRepoParamsMatchFilteredTools(t *testing.T) {
+ tests := []struct {
+ name string
+ safeOutputs *SafeOutputsConfig
+ toolName string
+ expectRepoParam bool
+ expectWildcard bool
+ }{
+ {
+ name: "create_issue with allowed-repos",
+ safeOutputs: &SafeOutputsConfig{
+ CreateIssues: &CreateIssuesConfig{
+ BaseSafeOutputConfig: BaseSafeOutputConfig{Max: strPtr("1")},
+ TargetRepoSlug: "org/default-repo",
+ AllowedRepos: []string{"org/other-repo"},
+ },
+ },
+ toolName: "create_issue",
+ expectRepoParam: true,
+ },
+ {
+ name: "update_issue with wildcard target-repo",
+ safeOutputs: &SafeOutputsConfig{
+ UpdateIssues: &UpdateIssuesConfig{
+ UpdateEntityConfig: UpdateEntityConfig{
+ SafeOutputTargetConfig: SafeOutputTargetConfig{
+ TargetRepoSlug: "*",
+ },
+ },
+ },
+ },
+ toolName: "update_issue",
+ expectRepoParam: true,
+ expectWildcard: true,
+ },
+ {
+ name: "create_issue without allowed-repos",
+ safeOutputs: &SafeOutputsConfig{
+ CreateIssues: &CreateIssuesConfig{
+ BaseSafeOutputConfig: BaseSafeOutputConfig{Max: strPtr("1")},
+ TargetRepoSlug: "org/target",
+ },
+ },
+ toolName: "create_issue",
+ expectRepoParam: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ data := &WorkflowData{SafeOutputs: tt.safeOutputs}
+
+ // Old approach: get repo param from embedded tools.json
+ filteredJSON, err := generateFilteredToolsJSON(data, ".github/workflows/test.md")
+ require.NoError(t, err, "generateFilteredToolsJSON should not error")
+
+ var filteredTools []map[string]any
+ require.NoError(t, json.Unmarshal([]byte(filteredJSON), &filteredTools), "filtered JSON should parse")
+
+ var oldTool map[string]any
+ for _, tool := range filteredTools {
+ if tool["name"] == tt.toolName {
+ oldTool = tool
+ break
+ }
+ }
+ require.NotNil(t, oldTool, "old approach should include tool %s", tt.toolName)
+
+ oldInputSchema, _ := oldTool["inputSchema"].(map[string]any)
+ oldProperties, _ := oldInputSchema["properties"].(map[string]any)
+ oldRepoParam, oldHasRepo := oldProperties["repo"]
+
+ // New approach
+ metaJSON, err := generateToolsMetaJSON(data, ".github/workflows/test.md")
+ require.NoError(t, err, "generateToolsMetaJSON should not error")
+
+ var meta ToolsMeta
+ require.NoError(t, json.Unmarshal([]byte(metaJSON), &meta), "meta JSON should parse")
+
+ newRepoParam, newHasRepo := meta.RepoParams[tt.toolName]
+
+ // Both old and new approaches should agree on whether repo param is present
+ assert.Equal(t, oldHasRepo, newHasRepo,
+ "old and new approaches should agree on repo param presence for %s", tt.toolName)
+
+ if tt.expectRepoParam {
+ require.True(t, newHasRepo, "tools_meta should include repo_param for %s", tt.toolName)
+
+ // Description should be consistent
+ oldRepoMap, _ := oldRepoParam.(map[string]any)
+ oldRepoDesc, _ := oldRepoMap["description"].(string)
+ newRepoDesc, _ := newRepoParam["description"].(string)
+ assert.Equal(t, oldRepoDesc, newRepoDesc, "repo param description should match between old and new")
+
+ if tt.expectWildcard {
+ assert.Contains(t, newRepoDesc, "Any repository can be targeted",
+ "wildcard repo param description should mention any repo")
+ }
+ } else {
+ assert.False(t, newHasRepo, "tools_meta should NOT include repo_param for %s", tt.toolName)
+ }
+ })
+ }
+}
+
+// TestToolsMetaJSONDynamicToolsFromCustomJobs verifies that custom safe-job tool
+// definitions are placed in dynamic_tools in tools_meta.json.
+func TestToolsMetaJSONDynamicToolsFromCustomJobs(t *testing.T) {
+ data := &WorkflowData{
+ SafeOutputs: &SafeOutputsConfig{
+ Jobs: map[string]*SafeJobConfig{
+ "deploy_app": {
+ Description: "Deploy the application",
+ Inputs: map[string]*InputDefinition{
+ "env": {
+ Type: "choice",
+ Options: []string{"staging", "production"},
+ Description: "Target environment",
+ Required: true,
+ },
+ },
+ },
+ },
+ },
+ }
+
+ // Old approach produces a single custom-job tool in filtered JSON
+ filteredJSON, err := generateFilteredToolsJSON(data, ".github/workflows/test.md")
+ require.NoError(t, err, "generateFilteredToolsJSON should not error")
+ var filteredTools []map[string]any
+ require.NoError(t, json.Unmarshal([]byte(filteredJSON), &filteredTools), "filtered JSON should parse")
+ require.Len(t, filteredTools, 1, "old approach should have exactly 1 tool")
+ assert.Equal(t, "deploy_app", filteredTools[0]["name"], "tool should be named deploy_app")
+
+ // New approach places it in dynamic_tools
+ metaJSON, err := generateToolsMetaJSON(data, ".github/workflows/test.md")
+ require.NoError(t, err, "generateToolsMetaJSON should not error")
+ var meta ToolsMeta
+ require.NoError(t, json.Unmarshal([]byte(metaJSON), &meta), "meta JSON should parse")
+
+ require.Len(t, meta.DynamicTools, 1, "tools_meta should have exactly 1 dynamic tool")
+ dynTool := meta.DynamicTools[0]
+ assert.Equal(t, "deploy_app", dynTool["name"], "dynamic tool name should match")
+ assert.Equal(t, "Deploy the application", dynTool["description"], "dynamic tool description should match")
+
+ inputSchema, ok := dynTool["inputSchema"].(map[string]any)
+ require.True(t, ok, "dynamic tool should have inputSchema")
+ properties, ok := inputSchema["properties"].(map[string]any)
+ require.True(t, ok, "inputSchema should have properties")
+ envProp, ok := properties["env"].(map[string]any)
+ require.True(t, ok, "env property should exist")
+ assert.Equal(t, "string", envProp["type"], "choice type should be mapped to string in JSON Schema")
+ assert.Equal(t, []any{"staging", "production"}, envProp["enum"], "enum values should match options")
+}
+
+// TestToolsMetaJSONCompiledWorkflowEmbedsMeta verifies end-to-end that a compiled lock file
+// contains tools_meta.json (not an inlined tools.json) with the right description constraints.
+func TestToolsMetaJSONCompiledWorkflowEmbedsMeta(t *testing.T) {
+ tmpDir := testutil.TempDir(t, "tools-meta-compiled-test")
+
+ testContent := `---
+on: push
+name: Test Compiled Meta
+engine: copilot
+safe-outputs:
+ create-issue:
+ max: 2
+ title-prefix: "[auto] "
+ labels:
+ - generated
+ create-pull-request:
+ max: 1
+ allowed-repos:
+ - org/other-repo
+ missing-tool: {}
+ noop: {}
+---
+
+Test workflow to verify tools_meta in compiled output.
+`
+
+ testFile := filepath.Join(tmpDir, "test-compiled-meta.md")
+ require.NoError(t, os.WriteFile(testFile, []byte(testContent), 0644), "should write test file")
+
+ compiler := NewCompiler()
+ require.NoError(t, compiler.CompileWorkflow(testFile), "compilation should succeed")
+
+ lockFile := filepath.Join(tmpDir, "test-compiled-meta.lock.yml")
+ yamlBytes, err := os.ReadFile(lockFile)
+ require.NoError(t, err, "should read lock file")
+ yamlStr := string(yamlBytes)
+
+ // Structural checks: new strategy is in use
+ assert.Contains(t, yamlStr, "tools_meta.json", "lock file should reference tools_meta.json")
+ assert.Contains(t, yamlStr, "generate_safe_outputs_tools.cjs", "lock file should invoke JS generator")
+ assert.NotContains(t, yamlStr, `cat > /opt/gh-aw/safeoutputs/tools.json`, "lock file should NOT inline tools.json")
+
+ // tools_meta.json must contain create_issue description suffix with constraint text
+ assert.Contains(t, yamlStr, "CONSTRAINTS:", "tools_meta should contain constraint text")
+ assert.Contains(t, yamlStr, "Maximum 2 issue", "tools_meta should contain max constraint for create_issue")
+ assert.Contains(t, yamlStr, `[auto]`, "tools_meta should contain title prefix for create_issue")
+ assert.Contains(t, yamlStr, `generated`, "tools_meta should contain label for create_issue")
+
+ // tools_meta.json must contain repo_params for create_pull_request (has allowed-repos)
+ assert.Contains(t, yamlStr, `"repo_params"`, "tools_meta should contain repo_params section")
+
+ meta := extractToolsMetaFromLockFile(t, yamlStr)
+
+ // Verify tools without constraints do NOT get description suffixes
+ _, hasMissingTool := meta.DescriptionSuffixes["missing_tool"]
+ assert.False(t, hasMissingTool, "missing_tool should not have a description suffix")
+ _, hasNoop := meta.DescriptionSuffixes["noop"]
+ assert.False(t, hasNoop, "noop should not have a description suffix")
+}
+
+// TestToolsMetaJSONPushRepoMemory verifies that push_repo_memory appears in
+// description_suffixes when repo-memory is configured.
+func TestToolsMetaJSONPushRepoMemory(t *testing.T) {
+ data := &WorkflowData{
+ SafeOutputs: &SafeOutputsConfig{},
+ RepoMemoryConfig: &RepoMemoryConfig{
+ Memories: []RepoMemoryEntry{
+ {ID: "default", MaxFileSize: 102400, MaxPatchSize: 10240, MaxFileCount: 100},
+ },
+ },
+ }
+
+ // Old approach includes push_repo_memory in filtered tools
+ filteredJSON, err := generateFilteredToolsJSON(data, ".github/workflows/test.md")
+ require.NoError(t, err, "generateFilteredToolsJSON should not error")
+ var filteredTools []map[string]any
+ require.NoError(t, json.Unmarshal([]byte(filteredJSON), &filteredTools), "filtered JSON should parse")
+ var foundOld bool
+ for _, tool := range filteredTools {
+ if tool["name"] == "push_repo_memory" {
+ foundOld = true
+ break
+ }
+ }
+ assert.True(t, foundOld, "old approach should include push_repo_memory when RepoMemoryConfig is set")
+
+ // New approach: push_repo_memory is a predefined static tool, so it appears in
+ // computeEnabledToolNames and its description suffix (if any) goes in tools_meta.
+ enabledTools := computeEnabledToolNames(data)
+ assert.True(t, enabledTools["push_repo_memory"], "computeEnabledToolNames should include push_repo_memory")
+}
+
+// TestToolsMetaJSONEmptyWhenNoSafeOutputs verifies that tools_meta.json is empty
+// (no description_suffixes, no repo_params, no dynamic_tools) when safe-outputs is nil.
+func TestToolsMetaJSONEmptyWhenNoSafeOutputs(t *testing.T) {
+ data := &WorkflowData{SafeOutputs: nil}
+
+ metaJSON, err := generateToolsMetaJSON(data, ".github/workflows/test.md")
+ require.NoError(t, err, "generateToolsMetaJSON should not error for nil safe-outputs")
+
+ var meta ToolsMeta
+ require.NoError(t, json.Unmarshal([]byte(metaJSON), &meta), "meta JSON should parse")
+
+ assert.Empty(t, meta.DescriptionSuffixes, "description_suffixes should be empty when no safe-outputs")
+ assert.Empty(t, meta.RepoParams, "repo_params should be empty when no safe-outputs")
+ assert.Empty(t, meta.DynamicTools, "dynamic_tools should be empty when no safe-outputs")
+}
+
+// TestToolsMetaJSONCustomDescriptionsAllToolTypes exercises all tool types that have
+// constraint-bearing configurations to ensure no tool type regresses silently.
+func TestToolsMetaJSONCustomDescriptionsAllToolTypes(t *testing.T) {
+ tests := []struct {
+ name string
+ safeOutputs *SafeOutputsConfig
+ toolName string
+ wantContains string
+ }{
+ {
+ name: "create_discussion with max and category",
+ safeOutputs: &SafeOutputsConfig{
+ CreateDiscussions: &CreateDiscussionsConfig{
+ BaseSafeOutputConfig: BaseSafeOutputConfig{Max: strPtr("2")},
+ Category: "announcements",
+ },
+ },
+ toolName: "create_discussion",
+ wantContains: `Discussions will be created in category "announcements"`,
+ },
+ {
+ name: "close_issue with max and required prefix",
+ safeOutputs: &SafeOutputsConfig{
+ CloseIssues: &CloseIssuesConfig{
+ BaseSafeOutputConfig: BaseSafeOutputConfig{Max: strPtr("5")},
+ SafeOutputFilterConfig: SafeOutputFilterConfig{
+ RequiredTitlePrefix: "[bot] ",
+ },
+ },
+ },
+ toolName: "close_issue",
+ wantContains: `Only issues with title prefix "[bot] "`,
+ },
+ {
+ name: "add_labels with allowed labels",
+ safeOutputs: &SafeOutputsConfig{
+ AddLabels: &AddLabelsConfig{
+ BaseSafeOutputConfig: BaseSafeOutputConfig{Max: strPtr("3")},
+ Allowed: []string{"bug", "enhancement"},
+ },
+ },
+ toolName: "add_labels",
+ wantContains: `Only these labels are allowed: ["bug" "enhancement"]`,
+ },
+ {
+ name: "update_project with project URL",
+ safeOutputs: &SafeOutputsConfig{
+ UpdateProjects: &UpdateProjectConfig{
+ BaseSafeOutputConfig: BaseSafeOutputConfig{Max: strPtr("1")},
+ Project: "https://github.com/orgs/org/projects/1",
+ },
+ },
+ toolName: "update_project",
+ wantContains: "https://github.com/orgs/org/projects/1",
+ },
+ {
+ name: "assign_to_agent with base branch",
+ safeOutputs: &SafeOutputsConfig{
+ AssignToAgent: &AssignToAgentConfig{
+ BaseSafeOutputConfig: BaseSafeOutputConfig{Max: strPtr("1")},
+ BaseBranch: "main",
+ SafeOutputTargetConfig: SafeOutputTargetConfig{
+ TargetRepoSlug: "org/target",
+ },
+ },
+ },
+ toolName: "assign_to_agent",
+ wantContains: `Pull requests will target the "main" branch`,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ data := &WorkflowData{SafeOutputs: tt.safeOutputs}
+
+ metaJSON, err := generateToolsMetaJSON(data, ".github/workflows/test.md")
+ require.NoError(t, err, "generateToolsMetaJSON should not error")
+
+ var meta ToolsMeta
+ require.NoError(t, json.Unmarshal([]byte(metaJSON), &meta), "meta JSON should parse")
+
+ suffix, ok := meta.DescriptionSuffixes[tt.toolName]
+ require.True(t, ok, "tools_meta should have description_suffix for %s", tt.toolName)
+ assert.Contains(t, suffix, tt.wantContains,
+ "description suffix for %s should contain %q\n got: %q", tt.toolName, tt.wantContains, suffix)
+
+ // Regression guard: verify generateFilteredToolsJSON (old approach) would produce
+ // the same constraint text in the tool's description.
+ filteredJSON, err := generateFilteredToolsJSON(data, ".github/workflows/test.md")
+ require.NoError(t, err, "generateFilteredToolsJSON should not error")
+
+ // Parse the JSON to access the actual description string (avoids JSON escape issues)
+ var filteredTools []map[string]any
+ require.NoError(t, json.Unmarshal([]byte(filteredJSON), &filteredTools), "filtered JSON should parse")
+ var oldTool map[string]any
+ for _, tool := range filteredTools {
+ if tool["name"] == tt.toolName {
+ oldTool = tool
+ break
+ }
+ }
+ require.NotNil(t, oldTool, "old filtered tools should include %s", tt.toolName)
+ oldDescription, ok := oldTool["description"].(string)
+ require.True(t, ok, "old tool description should be a string")
+ assert.Contains(t, oldDescription, tt.wantContains,
+ "old filtered JSON should also contain the constraint text for %s (regression guard)", tt.toolName)
+ })
+ }
+}
+
+// extractToolsMetaFromLockFile parses the tools_meta.json heredoc embedded in a compiled lock file.
+func extractToolsMetaFromLockFile(t *testing.T, yamlStr string) ToolsMeta {
+ t.Helper()
+
+ const delimiter = "GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF"
+ // Opening line is: cat > ... << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
+ openMarker := "<< '" + delimiter + "'\n"
+ start := strings.Index(yamlStr, openMarker)
+ require.NotEqual(t, -1, start, "lock file should contain tools_meta heredoc opening delimiter")
+ contentStart := start + len(openMarker)
+
+ // Closing line is: " GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF" (indented)
+ closeMarker := " " + delimiter + "\n"
+ end := strings.Index(yamlStr[contentStart:], closeMarker)
+ require.NotEqual(t, -1, end, "lock file should contain tools_meta heredoc closing delimiter")
+
+ raw := yamlStr[contentStart : contentStart+end]
+
+ // Strip YAML indentation (each line is indented with " ")
+ var sb strings.Builder
+ for _, line := range strings.Split(raw, "\n") {
+ sb.WriteString(strings.TrimPrefix(line, " "))
+ sb.WriteString("\n")
+ }
+
+ var meta ToolsMeta
+ require.NoError(t, json.Unmarshal([]byte(strings.TrimSpace(sb.String())), &meta),
+ "tools_meta.json from lock file should be valid JSON")
+ return meta
+}
diff --git a/pkg/workflow/safe_outputs_validation.go b/pkg/workflow/safe_outputs_validation.go
index 338f09d1aa3..cb466de0650 100644
--- a/pkg/workflow/safe_outputs_validation.go
+++ b/pkg/workflow/safe_outputs_validation.go
@@ -21,6 +21,12 @@ func (c *Compiler) validateNetworkAllowedDomains(network *NetworkPermissions) er
collector := NewErrorCollector(c.failFast)
for i, domain := range network.Allowed {
+ // "*" means allow all traffic - skip validation
+ if domain == "*" {
+ safeOutputsDomainsValidationLog.Print("Skipping allow-all wildcard '*'")
+ continue
+ }
+
// Skip ecosystem identifiers - they don't need domain pattern validation
if isEcosystemIdentifier(domain) {
safeOutputsDomainsValidationLog.Printf("Skipping ecosystem identifier: %s", domain)
@@ -44,11 +50,15 @@ func (c *Compiler) validateNetworkAllowedDomains(network *NetworkPermissions) er
return nil
}
+// isEcosystemIdentifierPattern matches valid ecosystem identifiers like "defaults", "node", "dev-tools"
+var isEcosystemIdentifierPattern = regexp.MustCompile(`^[a-z][a-z0-9-]*$`)
+
// isEcosystemIdentifier checks if a domain string is actually an ecosystem identifier
func isEcosystemIdentifier(domain string) bool {
- // Ecosystem identifiers don't contain dots and don't have protocol prefixes
- // They are simple identifiers like "defaults", "node", "python", etc.
- return !strings.Contains(domain, ".") && !strings.Contains(domain, "://")
+ // Ecosystem identifiers are simple lowercase alphanumeric identifiers with optional hyphens
+ // like "defaults", "node", "python", "dev-tools", "default-safe-outputs".
+ // They don't contain dots, protocol prefixes, spaces, wildcards, or other special characters.
+ return isEcosystemIdentifierPattern.MatchString(domain)
}
// domainPattern validates domain patterns including wildcards
@@ -61,7 +71,8 @@ func isEcosystemIdentifier(domain string) bool {
// - Empty or malformed domains
var domainPattern = regexp.MustCompile(`^(\*\.)?[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$`)
-// validateSafeOutputsAllowedDomains validates the allowed-domains configuration in safe-outputs
+// validateSafeOutputsAllowedDomains validates the allowed-domains configuration in safe-outputs.
+// Supports ecosystem identifiers (e.g., "python", "node", "default-safe-outputs") like network.allowed.
func (c *Compiler) validateSafeOutputsAllowedDomains(config *SafeOutputsConfig) error {
if config == nil || len(config.AllowedDomains) == 0 {
return nil
@@ -72,6 +83,12 @@ func (c *Compiler) validateSafeOutputsAllowedDomains(config *SafeOutputsConfig)
collector := NewErrorCollector(c.failFast)
for i, domain := range config.AllowedDomains {
+ // Skip ecosystem identifiers - they don't need domain pattern validation
+ if isEcosystemIdentifier(domain) {
+ safeOutputsDomainsValidationLog.Printf("Skipping ecosystem identifier: %s", domain)
+ continue
+ }
+
if err := validateDomainPattern(domain); err != nil {
wrappedErr := fmt.Errorf("safe-outputs.allowed-domains[%d]: %w", i, err)
if returnErr := collector.Add(wrappedErr); returnErr != nil {
diff --git a/pkg/workflow/safe_output_validation_config.go b/pkg/workflow/safe_outputs_validation_config.go
similarity index 99%
rename from pkg/workflow/safe_output_validation_config.go
rename to pkg/workflow/safe_outputs_validation_config.go
index b5eadeb67b1..86ad14581fb 100644
--- a/pkg/workflow/safe_output_validation_config.go
+++ b/pkg/workflow/safe_outputs_validation_config.go
@@ -6,7 +6,7 @@ import (
"github.com/github/gh-aw/pkg/logger"
)
-var safeOutputValidationLog = logger.New("workflow:safe_output_validation_config")
+var safeOutputValidationLog = logger.New("workflow:safe_outputs_validation_config")
// FieldValidation defines validation rules for a single field
type FieldValidation struct {
diff --git a/pkg/workflow/schedule_preprocessing.go b/pkg/workflow/schedule_preprocessing.go
index db346221cdf..09aff295578 100644
--- a/pkg/workflow/schedule_preprocessing.go
+++ b/pkg/workflow/schedule_preprocessing.go
@@ -128,6 +128,18 @@ func (c *Compiler) preprocessScheduleFields(frontmatter map[string]any, markdown
return nil
}
+ // Check if it's a label-command shorthand (starts with "label-command ")
+ if labelName, ok := strings.CutPrefix(onStr, "label-command "); ok {
+ labelName = strings.TrimSpace(labelName)
+ if labelName == "" {
+ return errors.New("label-command shorthand requires a label name after 'label-command'")
+ }
+ schedulePreprocessingLog.Printf("Converting shorthand 'on: %s' to label_command + workflow_dispatch", onStr)
+ onMap := expandLabelCommandShorthand(labelName)
+ frontmatter["on"] = onMap
+ return nil
+ }
+
// Check if it's a label trigger shorthand (labeled label1 label2...)
entityType, labelNames, isLabelTrigger, err := parseLabelTriggerShorthand(onStr)
if err != nil {
diff --git a/pkg/workflow/secret_validation_test.go b/pkg/workflow/secret_validation_test.go
index 91505e2caa9..faf338d7155 100644
--- a/pkg/workflow/secret_validation_test.go
+++ b/pkg/workflow/secret_validation_test.go
@@ -23,7 +23,7 @@ func TestGenerateMultiSecretValidationStep(t *testing.T) {
docsURL: "https://github.github.com/gh-aw/reference/engines/#openai-codex",
wantStrings: []string{
"Validate CODEX_API_KEY or OPENAI_API_KEY secret",
- "run: /opt/gh-aw/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex",
+ "run: " + GhAwHome + "/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex",
"CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}",
"OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}",
},
@@ -35,7 +35,7 @@ func TestGenerateMultiSecretValidationStep(t *testing.T) {
docsURL: "https://github.github.com/gh-aw/reference/engines/#github-copilot-default",
wantStrings: []string{
"Validate COPILOT_GITHUB_TOKEN secret",
- "run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default",
+ "run: " + GhAwHome + "/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default",
"COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}",
},
},
@@ -46,7 +46,7 @@ func TestGenerateMultiSecretValidationStep(t *testing.T) {
docsURL: "https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code",
wantStrings: []string{
"Validate ANTHROPIC_API_KEY secret",
- "run: /opt/gh-aw/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code",
+ "run: " + GhAwHome + "/actions/validate_multi_secret.sh ANTHROPIC_API_KEY 'Claude Code' https://github.github.com/gh-aw/reference/engines/#anthropic-claude-code",
"ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}",
},
},
@@ -64,7 +64,7 @@ func TestGenerateMultiSecretValidationStep(t *testing.T) {
}
// Verify it calls the validate_multi_secret.sh script
- if !strings.Contains(stepContent, "/opt/gh-aw/actions/validate_multi_secret.sh") {
+ if !strings.Contains(stepContent, GhAwHome+"/actions/validate_multi_secret.sh") {
t.Error("Expected step to call validate_multi_secret.sh script")
}
@@ -151,7 +151,7 @@ func TestCodexEngineHasSecretValidation(t *testing.T) {
}
// Should call the validate_multi_secret.sh script with both secret names
- if !strings.Contains(stepContent, "/opt/gh-aw/actions/validate_multi_secret.sh") {
+ if !strings.Contains(stepContent, GhAwHome+"/actions/validate_multi_secret.sh") {
t.Error("Should call validate_multi_secret.sh script")
}
if !strings.Contains(stepContent, "CODEX_API_KEY OPENAI_API_KEY") {
diff --git a/pkg/workflow/setup_action_paths.go b/pkg/workflow/setup_action_paths.go
index 16736ac89da..62b37dea736 100644
--- a/pkg/workflow/setup_action_paths.go
+++ b/pkg/workflow/setup_action_paths.go
@@ -1,5 +1,35 @@
package workflow
+import "fmt"
+
+// GhAwHome is the shell expression for GH_AW_HOME.
+// Use this in bash `run:` contexts where shell variable expansion occurs.
+// The job-level env sets GH_AW_HOME to /opt/gh-aw by default.
+const GhAwHome = "${GH_AW_HOME}"
+
+// GhAwHomeJS is the JavaScript expression for GH_AW_HOME.
+// Use this inside require() or other JS expressions in github-script steps.
+// The job-level env sets GH_AW_HOME to /opt/gh-aw by default.
+const GhAwHomeJS = "process.env.GH_AW_HOME"
+
+// GhAwHomeExpr is the GitHub Actions expression for GH_AW_HOME.
+// Use this in YAML env: blocks where shell variable expansion does NOT occur.
+// GitHub Actions evaluates ${{ env.VAR }} before passing values to steps.
+const GhAwHomeExpr = "${{ env.GH_AW_HOME }}"
+
+// GhAwHomeExprDefault is the GitHub Actions expression for GH_AW_HOME with a fallback.
+// Use this in job-level env: blocks so callers can override GH_AW_HOME via
+// workflow-level or repository-level env, while defaulting to GhAwHomeDefault.
+const GhAwHomeExprDefault = "${{ env.GH_AW_HOME || '/opt/gh-aw' }}"
+
// SetupActionDestination is the path where the setup action copies script files
// on the agent runner (e.g. /opt/gh-aw/actions).
-const SetupActionDestination = "/opt/gh-aw/actions"
+// Uses GitHub Actions expression syntax so the value is resolved before being passed
+// to setup.sh (via INPUT_DESTINATION in script mode, or destination: in dev/release mode).
+const SetupActionDestination = GhAwHomeExpr + "/actions"
+
+// JsRequireGhAw generates a JavaScript require() argument expression for a file
+// under GH_AW_HOME. The relativePath should be like "actions/foo.cjs".
+func JsRequireGhAw(relativePath string) string {
+ return fmt.Sprintf("%s + '/%s'", GhAwHomeJS, relativePath)
+}
diff --git a/pkg/workflow/step_order_validation.go b/pkg/workflow/step_order_validation.go
index 24c07a3b91f..77c55d80ed5 100644
--- a/pkg/workflow/step_order_validation.go
+++ b/pkg/workflow/step_order_validation.go
@@ -180,7 +180,7 @@ func (t *StepOrderTracker) findUnscannablePaths(artifactUploads []StepRecord) []
func isPathScannedBySecretRedaction(path string) bool {
// Paths must be under /tmp/gh-aw/ or /opt/gh-aw/ to be scanned
// Accept both literal paths and environment variable references
- if !strings.HasPrefix(path, "/tmp/gh-aw/") && !strings.HasPrefix(path, "/opt/gh-aw/") {
+ if !strings.HasPrefix(path, "/tmp/gh-aw/") && !strings.HasPrefix(path, "/opt/gh-aw/") && !strings.HasPrefix(path, "${GH_AW_HOME") {
// Check if it's an environment variable that might resolve to /tmp/gh-aw/ or /opt/gh-aw/
// For now, we'll allow ${{ env.* }} patterns through as we can't resolve them at compile time
// Assume environment variables that might contain /tmp/gh-aw or /opt/gh-aw paths are safe
diff --git a/pkg/workflow/step_order_validation_integration_test.go b/pkg/workflow/step_order_validation_integration_test.go
index 87a948d85b6..e21a32b45a1 100644
--- a/pkg/workflow/step_order_validation_integration_test.go
+++ b/pkg/workflow/step_order_validation_integration_test.go
@@ -174,7 +174,7 @@ This workflow uploads artifacts.
// Verify common upload paths are present and under /tmp/gh-aw/ or /opt/gh-aw/
uploadPaths := []string{
- "/opt/gh-aw/safeoutputs/outputs.jsonl",
+ "${GH_AW_HOME}/safeoutputs/outputs.jsonl",
"/tmp/gh-aw/agent-stdio.log",
"/tmp/gh-aw/mcp-logs/",
}
@@ -182,7 +182,7 @@ This workflow uploads artifacts.
for _, path := range uploadPaths {
if strings.Contains(contentStr, path) {
// Verify it's under /tmp/gh-aw/ or /opt/gh-aw/ (scannable paths)
- if !strings.HasPrefix(path, "/tmp/gh-aw/") && !strings.HasPrefix(path, "/opt/gh-aw/") {
+ if !strings.HasPrefix(path, "/tmp/gh-aw/") && !strings.HasPrefix(path, "/opt/gh-aw/") && !strings.HasPrefix(path, "${GH_AW_HOME") {
t.Errorf("Upload path %s is not under /tmp/gh-aw/ or /opt/gh-aw/ and won't be scanned", path)
}
}
diff --git a/pkg/workflow/step_order_validation_test.go b/pkg/workflow/step_order_validation_test.go
index bae1f3db3ec..256bcf4aaf1 100644
--- a/pkg/workflow/step_order_validation_test.go
+++ b/pkg/workflow/step_order_validation_test.go
@@ -109,8 +109,8 @@ func TestIsPathScannedBySecretRedaction_ScannableFiles(t *testing.T) {
expected: true,
},
{
- name: "JSONL file in /opt/gh-aw/",
- path: "/opt/gh-aw/safeoutputs/outputs.jsonl",
+ name: "JSONL file in ${GH_AW_HOME}/",
+ path: "${GH_AW_HOME}/safeoutputs/outputs.jsonl",
expected: true,
},
{
diff --git a/pkg/workflow/step_summary_test.go b/pkg/workflow/step_summary_test.go
index e25842be68f..e5db984dd13 100644
--- a/pkg/workflow/step_summary_test.go
+++ b/pkg/workflow/step_summary_test.go
@@ -134,7 +134,7 @@ This workflow tests that the step summary includes agentic run information.
}
// Verify that the generate_aw_info.cjs helper is invoked from the step
- if !strings.Contains(lockContent, "require('/opt/gh-aw/actions/generate_aw_info.cjs')") {
+ if !strings.Contains(lockContent, "require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs')") {
t.Error("Expected generate_aw_info.cjs require call in 'Generate agentic run info' step")
}
@@ -240,7 +240,7 @@ This workflow tests the workflow overview for Claude engine.
}
// Verify workflow overview call is present in the generate_aw_info step
- if !strings.Contains(lockContent, "require('/opt/gh-aw/actions/generate_aw_info.cjs')") {
+ if !strings.Contains(lockContent, "require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs')") {
t.Error("Expected generate_aw_info.cjs require call inside 'Generate agentic run info' step")
}
diff --git a/pkg/workflow/temp_folder_test.go b/pkg/workflow/temp_folder_test.go
index 0649b15cea8..2585c328b69 100644
--- a/pkg/workflow/temp_folder_test.go
+++ b/pkg/workflow/temp_folder_test.go
@@ -56,7 +56,7 @@ This is a test workflow to verify temp folder instructions are included.
}
// Test 2: Verify the cat command for temp folder prompt file is included
- if !strings.Contains(lockStr, "cat \"/opt/gh-aw/prompts/temp_folder_prompt.md\"") {
+ if !strings.Contains(lockStr, "cat \"${GH_AW_HOME}/prompts/temp_folder_prompt.md\"") {
t.Error("Expected cat command for temp folder prompt file in generated workflow")
}
diff --git a/pkg/workflow/template.go b/pkg/workflow/template.go
index 9bea3393b48..eaa81d6eb1f 100644
--- a/pkg/workflow/template.go
+++ b/pkg/workflow/template.go
@@ -123,8 +123,8 @@ func (c *Compiler) generateInterpolationAndTemplateStep(yaml *strings.Builder, e
// Load interpolate_prompt script from external file
// Use setup_globals helper to store GitHub Actions objects in global scope
- yaml.WriteString(" const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');\n")
+ yaml.WriteString(" const { setupGlobals } = require(" + JsRequireGhAw("actions/setup_globals.cjs") + ");\n")
yaml.WriteString(" setupGlobals(core, github, context, exec, io);\n")
- yaml.WriteString(" const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');\n")
+ yaml.WriteString(" const { main } = require(" + JsRequireGhAw("actions/interpolate_prompt.cjs") + ");\n")
yaml.WriteString(" await main();\n")
}
diff --git a/pkg/workflow/template_injection_validation_test.go b/pkg/workflow/template_injection_validation_test.go
index ead5484da06..287a340f931 100644
--- a/pkg/workflow/template_injection_validation_test.go
+++ b/pkg/workflow/template_injection_validation_test.go
@@ -75,7 +75,7 @@ func TestValidateNoTemplateInjection(t *testing.T) {
steps:
- name: Unsafe usage
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh ${{ steps.start-mcp-gateway.outputs.gateway-pid }}`,
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh ${{ steps.start-mcp-gateway.outputs.gateway-pid }}`,
shouldError: true,
errorString: "steps.*.outputs",
},
@@ -368,7 +368,7 @@ func TestTemplateInjectionRealWorldPatterns(t *testing.T) {
MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh ${{ steps.start-mcp-gateway.outputs.gateway-pid }}`
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh ${{ steps.start-mcp-gateway.outputs.gateway-pid }}`
err := validateNoTemplateInjection(yaml)
require.Error(t, err, "Should detect unsafe gateway-pid usage in run command")
@@ -390,7 +390,7 @@ func TestTemplateInjectionRealWorldPatterns(t *testing.T) {
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"`
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"`
err := validateNoTemplateInjection(yaml)
assert.NoError(t, err, "Should pass with gateway-pid in env variable")
diff --git a/pkg/workflow/template_rendering_test.go b/pkg/workflow/template_rendering_test.go
index d3d003a6d61..ddbafb8b907 100644
--- a/pkg/workflow/template_rendering_test.go
+++ b/pkg/workflow/template_rendering_test.go
@@ -118,7 +118,7 @@ Normal content here.
}
// Verify the setupGlobals helper is used
- if !strings.Contains(compiledStr, "const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs')") {
+ if !strings.Contains(compiledStr, "const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs')") {
t.Error("Template rendering step should use setupGlobals helper")
}
@@ -127,7 +127,7 @@ Normal content here.
}
// Verify the interpolate_prompt script is loaded via require
- if !strings.Contains(compiledStr, "const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs')") {
+ if !strings.Contains(compiledStr, "const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs')") {
t.Error("Template rendering step should require interpolate_prompt.cjs")
}
diff --git a/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/basic-copilot.golden b/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/basic-copilot.golden
index 349ad75bfa7..63c9a1995f8 100644
--- a/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/basic-copilot.golden
+++ b/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/basic-copilot.golden
@@ -32,7 +32,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -47,18 +47,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -76,9 +76,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "basic-copilot.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -92,14 +92,14 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
cat << 'GH_AW_PROMPT_EOF'
The following GitHub context information is available for this workflow:
@@ -130,6 +130,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -146,9 +147,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -165,10 +166,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -188,11 +189,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -209,6 +210,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: basiccopilot
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -225,13 +227,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -254,16 +260,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -272,14 +278,15 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0
- name: Start MCP Gateway
id: start-mcp-gateway
env:
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -297,10 +304,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -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 -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -308,10 +315,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
}
},
@@ -329,7 +341,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -338,7 +351,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -365,7 +378,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -403,15 +416,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -421,7 +434,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Parse agent logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -429,18 +442,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -491,7 +504,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -500,8 +513,8 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
diff --git a/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/smoke-copilot.golden b/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/smoke-copilot.golden
index 8dc75d25853..e7d2f991047 100644
--- a/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/smoke-copilot.golden
+++ b/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/smoke-copilot.golden
@@ -46,7 +46,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -61,18 +61,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","node","github","playwright"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -90,18 +90,18 @@ jobs:
GH_AW_WORKFLOW_FILE: "smoke-copilot.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Compute current body text
id: sanitized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/compute_text.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/compute_text.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -116,15 +116,16 @@ jobs:
GH_AW_GITHUB_SERVER_URL: ${{ github.server_url }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/playwright_prompt.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/playwright_prompt.md"
+ cat "${GH_AW_HOME}/prompts/agentic_workflows_guide.md"
cat << 'GH_AW_PROMPT_EOF'
The following GitHub context information is available for this workflow:
@@ -155,6 +156,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -228,9 +230,9 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -248,10 +250,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -272,11 +274,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -297,6 +299,7 @@ jobs:
issues: read
pull-requests: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: smokecopilot
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -313,7 +316,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -356,7 +359,11 @@ jobs:
- name: Capture GOROOT for AWF chroot mode
run: echo "GOROOT=$(go env GOROOT)" >> "$GITHUB_ENV"
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -379,16 +386,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -397,10 +404,10 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest mcr.microsoft.com/playwright/mcp
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 ghcr.io/github/serena-mcp-server:latest mcr.microsoft.com/playwright/mcp
- name: Install gh-aw extension
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -415,12 +422,12 @@ jobs:
fi
gh aw --version
# Copy the gh-aw binary to /opt/gh-aw for MCP server containerization
- mkdir -p /opt/gh-aw
+ mkdir -p ${GH_AW_HOME}
GH_AW_BIN=$(which gh-aw 2>/dev/null || find ~/.local/share/gh/extensions/gh-aw -name 'gh-aw' -type f 2>/dev/null | head -1)
if [ -n "$GH_AW_BIN" ] && [ -f "$GH_AW_BIN" ]; then
- cp "$GH_AW_BIN" /opt/gh-aw/gh-aw
- chmod +x /opt/gh-aw/gh-aw
- echo "Copied gh-aw binary to /opt/gh-aw/gh-aw"
+ cp "$GH_AW_BIN" ${GH_AW_HOME}/gh-aw
+ chmod +x ${GH_AW_HOME}/gh-aw
+ echo "Copied gh-aw binary to ${GH_AW_HOME}/gh-aw"
else
echo "::error::Failed to find gh-aw binary for MCP server"
exit 1
@@ -428,7 +435,8 @@ jobs:
- name: Start MCP Gateway
id: start-mcp-gateway
env:
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
@@ -448,10 +456,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -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 -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"agenticworkflows": {
@@ -464,6 +472,13 @@ jobs:
"GITHUB_TOKEN": "\${GITHUB_TOKEN}",
"GITHUB_ACTOR": "\${GITHUB_ACTOR}",
"GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
},
"github": {
@@ -471,10 +486,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"playwright": {
@@ -482,7 +502,14 @@ jobs:
"container": "mcr.microsoft.com/playwright/mcp",
"args": ["--init", "--network", "host", "--security-opt", "seccomp=unconfined", "--ipc=host"],
"entrypointArgs": ["--output-dir", "/tmp/gh-aw/mcp-logs/playwright", "--no-sandbox"],
- "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"]
+ "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
},
"serena": {
"type": "stdio",
@@ -490,7 +517,14 @@ jobs:
"args": ["--network", "host"],
"entrypoint": "serena",
"entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"],
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
+ "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"],
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
}
},
"gateway": {
@@ -507,7 +541,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -516,7 +551,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,googleapis.deno.dev,googlechromelabs.github.io,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deno.land,docs.github.com,esm.sh,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,googleapis.deno.dev,googlechromelabs.github.io,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -543,7 +578,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -581,15 +616,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -599,7 +634,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Parse agent logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -607,18 +642,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -669,7 +704,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -678,8 +713,8 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
diff --git a/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/with-imports.golden b/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/with-imports.golden
index 8410246bdb8..fd7d944146c 100644
--- a/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/with-imports.golden
+++ b/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/with-imports.golden
@@ -32,7 +32,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Generate agentic run info
id: generate_aw_info
env:
@@ -47,18 +47,18 @@ jobs:
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.24.1"
+ GH_AW_INFO_AWF_VERSION: "v0.24.2"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/generate_aw_info.cjs');
await main(core, context);
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ run: ${GH_AW_HOME}/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
@@ -76,9 +76,9 @@ jobs:
GH_AW_WORKFLOW_FILE: "with-imports.lock.yml"
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_workflow_timestamp_api.cjs');
await main();
- name: Create prompt with built-in context
env:
@@ -92,14 +92,14 @@ jobs:
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash ${GH_AW_HOME}/actions/create_prompt_first.sh
{
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
+ cat "${GH_AW_HOME}/prompts/xpia.md"
+ cat "${GH_AW_HOME}/prompts/temp_folder_prompt.md"
+ cat "${GH_AW_HOME}/prompts/markdown.md"
cat << 'GH_AW_PROMPT_EOF'
The following GitHub context information is available for this workflow:
@@ -130,6 +130,7 @@ jobs:
GH_AW_PROMPT_EOF
+ cat "${GH_AW_HOME}/prompts/github_mcp_tools_prompt.md"
cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
@@ -149,9 +150,9 @@ jobs:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -168,10 +169,10 @@ jobs:
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require(process.env.GH_AW_HOME + '/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -191,11 +192,11 @@ jobs:
- name: Validate prompt placeholders
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
+ run: bash ${GH_AW_HOME}/actions/validate_prompt_placeholders.sh
- name: Print prompt
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
+ run: bash ${GH_AW_HOME}/actions/print_prompt_summary.sh
- name: Upload activation artifact
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -212,6 +213,7 @@ jobs:
permissions:
contents: read
env:
+ GH_AW_HOME: ${{ env.GH_AW_HOME || '/opt/gh-aw' }}
GH_AW_WORKFLOW_ID_SANITIZED: withimports
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
@@ -228,13 +230,17 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ run: |
+ bash ${GH_AW_HOME}/actions/create_gh_aw_tmp_dir.sh
+ echo "GH_AW_SAFE_OUTPUTS=${GH_AW_HOME}/safeoutputs/outputs.jsonl" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${GH_AW_HOME}/safeoutputs/config.json" >> "$GITHUB_ENV"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${GH_AW_HOME}/safeoutputs/tools.json" >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -257,16 +263,16 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
+ run: ${GH_AW_HOME}/actions/install_copilot_cli.sh latest
env:
GH_HOST: github.com
- name: Install AWF binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1
+ run: bash ${GH_AW_HOME}/actions/install_awf_binary.sh v0.24.2
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -275,14 +281,15 @@ jobs:
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require(process.env.GH_AW_HOME + '/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0
+ run: bash ${GH_AW_HOME}/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0
- name: Start MCP Gateway
id: start-mcp-gateway
env:
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
@@ -300,10 +307,10 @@ jobs:
export DEBUG="*"
export GH_AW_ENGINE="copilot"
- export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -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 -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ cat << GH_AW_MCP_CONFIG_EOF | bash ${GH_AW_HOME}/actions/start_mcp_gateway.sh
{
"mcpServers": {
"github": {
@@ -311,10 +318,15 @@ jobs:
"container": "ghcr.io/github/github-mcp-server:v0.32.0",
"env": {
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
}
},
@@ -332,7 +344,8 @@ jobs:
name: activation
path: /tmp/gh-aw
- name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ continue-on-error: true
+ run: bash ${GH_AW_HOME}/actions/clean_git_credentials.sh
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -341,7 +354,7 @@ jobs:
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
# shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.1 --skip-pull --enable-api-proxy \
+ sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
@@ -368,7 +381,7 @@ jobs:
id: detect-inference-error
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: bash ${GH_AW_HOME}/actions/detect_inference_access_error.sh
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
@@ -406,15 +419,15 @@ jobs:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
+ bash ${GH_AW_HOME}/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/redact_secrets.cjs');
await main();
env:
GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
@@ -424,7 +437,7 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ run: bash ${GH_AW_HOME}/actions/append_agent_step_summary.sh
- name: Parse agent logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -432,18 +445,18 @@ jobs:
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -494,7 +507,7 @@ jobs:
- name: Setup Scripts
uses: ./actions/setup
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ env.GH_AW_HOME }}/actions
- name: Check team membership for workflow
id: check_membership
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -503,8 +516,8 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
+ const { setupGlobals } = require(process.env.GH_AW_HOME + '/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_membership.cjs');
+ const { main } = require(process.env.GH_AW_HOME + '/actions/check_membership.cjs');
await main();
diff --git a/pkg/workflow/testdata/wasm_golden/fixtures/shared/safe-output-app.md b/pkg/workflow/testdata/wasm_golden/fixtures/shared/safe-output-app.md
new file mode 100644
index 00000000000..944b17c5a94
--- /dev/null
+++ b/pkg/workflow/testdata/wasm_golden/fixtures/shared/safe-output-app.md
@@ -0,0 +1,31 @@
+---
+#safe-outputs:
+# app:
+# app-id: ${{ vars.APP_ID }}
+# private-key: ${{ secrets.APP_PRIVATE_KEY }}
+---
+
+
diff --git a/pkg/workflow/threat_detection.go b/pkg/workflow/threat_detection.go
index e72f490661d..3ebfe4940ea 100644
--- a/pkg/workflow/threat_detection.go
+++ b/pkg/workflow/threat_detection.go
@@ -287,9 +287,9 @@ func (c *Compiler) buildThreatDetectionAnalysisStep(data *WorkflowData) []string
func (c *Compiler) buildSetupScriptRequire() string {
// Build a simple require statement that calls the main function
// The template is now read from file at runtime by the JavaScript module
- script := `const { setupGlobals } = require('` + SetupActionDestination + `/setup_globals.cjs');
+ script := `const { setupGlobals } = require(` + JsRequireGhAw("actions/setup_globals.cjs") + `);
setupGlobals(core, github, context, exec, io);
-const { main } = require('` + SetupActionDestination + `/setup_threat_detection.cjs');
+const { main } = require(` + JsRequireGhAw("actions/setup_threat_detection.cjs") + `);
await main();`
return script
@@ -434,9 +434,9 @@ func (c *Compiler) buildWorkflowContextEnvVars(data *WorkflowData) []string {
// buildResultsParsingScriptRequire creates the parsing script that requires the .cjs module
func (c *Compiler) buildResultsParsingScriptRequire() string {
// Build a simple require statement that calls the main function
- script := `const { setupGlobals } = require('` + SetupActionDestination + `/setup_globals.cjs');
+ script := `const { setupGlobals } = require(` + JsRequireGhAw("actions/setup_globals.cjs") + `);
setupGlobals(core, github, context, exec, io);
-const { main } = require('` + SetupActionDestination + `/parse_threat_detection_results.cjs');
+const { main } = require(` + JsRequireGhAw("actions/parse_threat_detection_results.cjs") + `);
await main();`
return script
diff --git a/pkg/workflow/threat_detection_test.go b/pkg/workflow/threat_detection_test.go
index 1bc30954d98..ca7d51ae078 100644
--- a/pkg/workflow/threat_detection_test.go
+++ b/pkg/workflow/threat_detection_test.go
@@ -621,7 +621,7 @@ func TestSetupScriptReferencesPromptFile(t *testing.T) {
script := compiler.buildSetupScriptRequire()
// Verify the script uses require to load setup_threat_detection.cjs
- if !strings.Contains(script, "require('"+SetupActionDestination+"/setup_threat_detection.cjs')") {
+ if !strings.Contains(script, "require("+JsRequireGhAw("actions/setup_threat_detection.cjs")+")") {
t.Error("Expected setup script to require setup_threat_detection.cjs")
}
diff --git a/pkg/workflow/tools.go b/pkg/workflow/tools.go
index 0954a0cf015..eef5ff7293d 100644
--- a/pkg/workflow/tools.go
+++ b/pkg/workflow/tools.go
@@ -22,11 +22,14 @@ func (c *Compiler) applyDefaults(data *WorkflowData, markdownPath string) error
// Check if this is a command trigger workflow (by checking if user specified "on.command")
isCommandTrigger := false
+ isLabelCommandTrigger := false
if data.On == "" {
// parseOnSection may have already detected the command trigger and populated data.Command
// (this covers slash_command map format, slash_command shorthand "on: /name", and deprecated "command:")
if len(data.Command) > 0 {
isCommandTrigger = true
+ } else if len(data.LabelCommand) > 0 {
+ isLabelCommandTrigger = true
} else {
// Check the original frontmatter for command trigger
content, err := os.ReadFile(markdownPath)
@@ -40,6 +43,8 @@ func (c *Compiler) applyDefaults(data *WorkflowData, markdownPath string) error
isCommandTrigger = true
} else if _, hasCommand := onMap["command"]; hasCommand {
isCommandTrigger = true
+ } else if _, hasLabelCommand := onMap["label_command"]; hasLabelCommand {
+ isLabelCommandTrigger = true
}
}
}
@@ -72,6 +77,33 @@ func (c *Compiler) applyDefaults(data *WorkflowData, markdownPath string) error
maps.Copy(commandEventsMap, data.CommandOtherEvents)
}
+ // If label_command is also configured alongside slash_command, merge label events
+ // into the existing command events map to avoid duplicate YAML keys.
+ if len(data.LabelCommand) > 0 {
+ labelEventNames := FilterLabelCommandEvents(data.LabelCommandEvents)
+ for _, eventName := range labelEventNames {
+ if existingAny, ok := commandEventsMap[eventName]; ok {
+ if existingMap, ok := existingAny.(map[string]any); ok {
+ switch t := existingMap["types"].(type) {
+ case []string:
+ newTypes := make([]any, len(t)+1)
+ for i, s := range t {
+ newTypes[i] = s
+ }
+ newTypes[len(t)] = "labeled"
+ existingMap["types"] = newTypes
+ case []any:
+ existingMap["types"] = append(t, "labeled")
+ }
+ }
+ } else {
+ commandEventsMap[eventName] = map[string]any{
+ "types": []any{"labeled"},
+ }
+ }
+ }
+ }
+
// Convert merged events to YAML
mergedEventsYAML, err := yaml.Marshal(map[string]any{"on": commandEventsMap})
if err == nil {
@@ -112,7 +144,97 @@ func (c *Compiler) applyDefaults(data *WorkflowData, markdownPath string) error
}
if data.If == "" {
- data.If = commandConditionTree.Render()
+ if len(data.LabelCommand) > 0 {
+ // Combine: (slash_command condition) OR (label_command condition)
+ // This allows the workflow to activate via either mechanism.
+ labelConditionTree, err := buildLabelCommandCondition(data.LabelCommand, data.LabelCommandEvents, false)
+ if err != nil {
+ return fmt.Errorf("failed to build combined label-command condition: %w", err)
+ }
+ combined := &OrNode{Left: commandConditionTree, Right: labelConditionTree}
+ data.If = combined.Render()
+ } else {
+ data.If = commandConditionTree.Render()
+ }
+ }
+ } else if isLabelCommandTrigger {
+ toolsLog.Print("Workflow is label-command trigger, configuring label events")
+
+ // Build the label-command events map
+ // Generate events: issues, pull_request, discussion with types: [labeled]
+ filteredEvents := FilterLabelCommandEvents(data.LabelCommandEvents)
+ labelEventsMap := make(map[string]any)
+ for _, eventName := range filteredEvents {
+ labelEventsMap[eventName] = map[string]any{
+ "types": []any{"labeled"},
+ }
+ }
+
+ // Add workflow_dispatch with item_number input for manual testing
+ labelEventsMap["workflow_dispatch"] = map[string]any{
+ "inputs": map[string]any{
+ "item_number": map[string]any{
+ "description": "The number of the issue, pull request, or discussion",
+ "required": true,
+ "type": "string",
+ },
+ },
+ }
+ // Signal that this workflow has a dispatch item_number input so that
+ // applyWorkflowDispatchFallbacks and concurrency key building add the
+ // necessary inputs.item_number fallbacks for manual workflow_dispatch runs.
+ data.HasDispatchItemNumber = true
+
+ // Merge other events (if any) — this handles the no-clash requirement:
+ // if the user also has e.g. "issues: {types: [labeled], names: [bug]}" as a
+ // regular label trigger alongside label_command, merge the "types" arrays
+ // rather than generating a duplicate "issues:" block or silently dropping config.
+ if len(data.LabelCommandOtherEvents) > 0 {
+ for eventKey, eventVal := range data.LabelCommandOtherEvents {
+ if existing, exists := labelEventsMap[eventKey]; exists {
+ // Merge types arrays from user config into the label_command-generated entry.
+ existingMap, _ := existing.(map[string]any)
+ userMap, _ := eventVal.(map[string]any)
+ if existingMap != nil && userMap != nil {
+ existingTypes, _ := existingMap["types"].([]any)
+ userTypes, _ := userMap["types"].([]any)
+ merged := make([]any, 0, len(existingTypes)+len(userTypes))
+ merged = append(merged, existingTypes...)
+ merged = append(merged, userTypes...)
+ existingMap["types"] = merged
+ // Other fields (names, branches, etc.) from the user config are preserved.
+ for k, v := range userMap {
+ if k != "types" {
+ existingMap[k] = v
+ }
+ }
+ }
+ } else {
+ labelEventsMap[eventKey] = eventVal
+ }
+ }
+ }
+
+ // Convert merged events to YAML
+ mergedEventsYAML, err := yaml.Marshal(map[string]any{"on": labelEventsMap})
+ if err != nil {
+ return fmt.Errorf("failed to marshal label-command events: %w", err)
+ }
+ yamlStr := strings.TrimSuffix(string(mergedEventsYAML), "\n")
+ yamlStr = parser.QuoteCronExpressions(yamlStr)
+ // Pass frontmatter so label names in "names:" fields get commented out
+ yamlStr = c.commentOutProcessedFieldsInOnSection(yamlStr, map[string]any{})
+ data.On = yamlStr
+
+ // Build the label-command condition
+ hasOtherEvents := len(data.LabelCommandOtherEvents) > 0
+ labelConditionTree, err := buildLabelCommandCondition(data.LabelCommand, data.LabelCommandEvents, hasOtherEvents)
+ if err != nil {
+ return fmt.Errorf("failed to build label-command condition: %w", err)
+ }
+
+ if data.If == "" {
+ data.If = labelConditionTree.Render()
}
} else {
data.On = `on:
@@ -141,7 +263,7 @@ func (c *Compiler) applyDefaults(data *WorkflowData, markdownPath string) error
}
// Generate concurrency configuration using the dedicated concurrency module
- data.Concurrency = GenerateConcurrencyConfig(data, isCommandTrigger)
+ data.Concurrency = GenerateConcurrencyConfig(data, isCommandTrigger || isLabelCommandTrigger)
if data.RunName == "" {
data.RunName = fmt.Sprintf(`run-name: "%s"`, data.Name)
diff --git a/pkg/workflow/unified_prompt_step.go b/pkg/workflow/unified_prompt_step.go
index d045c046345..165eccbf762 100644
--- a/pkg/workflow/unified_prompt_step.go
+++ b/pkg/workflow/unified_prompt_step.go
@@ -255,7 +255,16 @@ func (c *Compiler) collectPromptSections(data *WorkflowData) []PromptSection {
})
}
- // 4. Trial mode note (if in trial mode)
+ // 4. Agentic Workflows MCP guide (if agentic-workflows tool is enabled)
+ if hasAgenticWorkflowsTool(data.ParsedTools) {
+ unifiedPromptLog.Print("Adding agentic-workflows guide section")
+ sections = append(sections, PromptSection{
+ Content: agenticWorkflowsGuideFile,
+ IsFile: true,
+ })
+ }
+
+ // 5. Trial mode note (if in trial mode)
if c.trialMode {
unifiedPromptLog.Print("Adding trial mode section")
trialContent := fmt.Sprintf("## Note\nThis workflow is running in directory $GITHUB_WORKSPACE, but that directory actually contains the contents of the repository '%s'.", c.trialLogicalRepoSlug)
@@ -265,7 +274,7 @@ func (c *Compiler) collectPromptSections(data *WorkflowData) []PromptSection {
})
}
- // 5. Cache memory instructions (if enabled)
+ // 6. Cache memory instructions (if enabled)
if data.CacheMemoryConfig != nil && len(data.CacheMemoryConfig.Caches) > 0 {
unifiedPromptLog.Printf("Adding cache memory section: caches=%d", len(data.CacheMemoryConfig.Caches))
section := buildCacheMemoryPromptSection(data.CacheMemoryConfig)
@@ -274,7 +283,7 @@ func (c *Compiler) collectPromptSections(data *WorkflowData) []PromptSection {
}
}
- // 6. Repo memory instructions (if enabled)
+ // 7. Repo memory instructions (if enabled)
if data.RepoMemoryConfig != nil && len(data.RepoMemoryConfig.Memories) > 0 {
unifiedPromptLog.Printf("Adding repo memory section: memories=%d", len(data.RepoMemoryConfig.Memories))
section := buildRepoMemoryPromptSection(data.RepoMemoryConfig)
@@ -283,7 +292,7 @@ func (c *Compiler) collectPromptSections(data *WorkflowData) []PromptSection {
}
}
- // 7. Safe outputs instructions (if enabled)
+ // 8. Safe outputs instructions (if enabled)
if HasSafeOutputsEnabled(data.SafeOutputs) {
unifiedPromptLog.Print("Adding safe outputs section")
// Static intro from file (gh CLI warning, temporary ID rules, noop note)
@@ -294,7 +303,7 @@ func (c *Compiler) collectPromptSections(data *WorkflowData) []PromptSection {
// Per-tool sections: opening tag + tools list (inline), tool instruction files, closing tag
sections = append(sections, buildSafeOutputsSections(data.SafeOutputs)...)
}
- // 8. GitHub context (if GitHub tool is enabled)
+ // 9. GitHub context (if GitHub tool is enabled)
if hasGitHubTool(data.ParsedTools) {
unifiedPromptLog.Print("Adding GitHub context section")
@@ -331,9 +340,23 @@ func (c *Compiler) collectPromptSections(data *WorkflowData) []PromptSection {
EnvVars: envVars,
})
}
+
+ // GitHub MCP tool-use guidance: clarifies that the MCP server is read-only and
+ // directs the model to use it for GitHub reads. When safe-outputs is also enabled,
+ // the guidance explicitly separates reads (GitHub MCP) from writes (safeoutputs) so
+ // the model is never steered away from the available read tools.
+ unifiedPromptLog.Print("Adding GitHub MCP tool-use guidance")
+ githubMCPFile := githubMCPToolsPromptFile
+ if HasSafeOutputsEnabled(data.SafeOutputs) {
+ githubMCPFile = githubMCPToolsWithSafeOutputsPromptFile
+ }
+ sections = append(sections, PromptSection{
+ Content: githubMCPFile,
+ IsFile: true,
+ })
}
- // 9. PR context (if comment-related triggers and checkout is needed)
+ // 10. PR context (if comment-related triggers and checkout is needed)
hasCommentTriggers := c.hasCommentRelatedTriggers(data)
needsCheckout := c.shouldAddCheckoutStep(data)
permParser := NewPermissionsParser(data.Permissions)
@@ -455,7 +478,7 @@ func (c *Compiler) generateUnifiedPromptCreationStep(yaml *strings.Builder, buil
}
yaml.WriteString(" run: |\n")
- yaml.WriteString(" bash /opt/gh-aw/actions/create_prompt_first.sh\n")
+ yaml.WriteString(" bash " + GhAwHome + "/actions/create_prompt_first.sh\n")
yaml.WriteString(" {\n")
// Track if we're inside a heredoc
@@ -717,6 +740,9 @@ func buildSafeOutputsSections(safeOutputs *SafeOutputsConfig) []PromptSection {
if safeOutputs.DispatchWorkflow != nil {
tools = append(tools, "dispatch_workflow")
}
+ if safeOutputs.CallWorkflow != nil {
+ tools = append(tools, "call_workflow")
+ }
if safeOutputs.MissingTool != nil {
tools = append(tools, "missing_tool")
}
diff --git a/pkg/workflow/unified_prompt_step_test.go b/pkg/workflow/unified_prompt_step_test.go
index 1fbe992ef61..d90832aa634 100644
--- a/pkg/workflow/unified_prompt_step_test.go
+++ b/pkg/workflow/unified_prompt_step_test.go
@@ -538,3 +538,89 @@ func TestCollectPromptSections_DisableXPIA(t *testing.T) {
assert.True(t, hasXPIA, "XPIA section should be included when feature flag is explicitly false")
})
}
+
+// TestCollectPromptSections_GitHubMCPAndSafeOutputsConsistency is a regression test that
+// ensures the generated prompt never assigns "all GitHub operations" to safeoutputs when
+// the GitHub MCP server is also mounted, and that GitHub MCP read guidance is always present.
+func TestCollectPromptSections_GitHubMCPAndSafeOutputsConsistency(t *testing.T) {
+ t.Run("both GitHub MCP and safe-outputs enabled", func(t *testing.T) {
+ compiler := &Compiler{}
+
+ data := &WorkflowData{
+ ParsedTools: NewTools(map[string]any{"github": true}),
+ SafeOutputs: &SafeOutputsConfig{
+ MissingData: &MissingDataConfig{},
+ NoOp: &NoOpConfig{},
+ },
+ }
+
+ sections := compiler.collectPromptSections(data)
+ require.NotEmpty(t, sections, "Should collect sections")
+
+ // No inline section should claim safeoutputs handles "all GitHub operations"
+ for _, section := range sections {
+ if !section.IsFile {
+ assert.NotContains(t, section.Content, "all GitHub operations",
+ "Prompt must not claim safeoutputs handles all GitHub operations when GitHub MCP is mounted")
+ }
+ }
+
+ // The with-safeoutputs variant of the GitHub MCP guidance file must be selected
+ var githubMCPSection *PromptSection
+ for i := range sections {
+ if sections[i].IsFile && strings.Contains(sections[i].Content, "github_mcp_tools") {
+ githubMCPSection = §ions[i]
+ break
+ }
+ }
+ require.NotNil(t, githubMCPSection, "Should include github_mcp_tools file when GitHub MCP is enabled")
+ assert.Equal(t, githubMCPToolsWithSafeOutputsPromptFile, githubMCPSection.Content,
+ "Should use the with-safeoutputs variant when both GitHub MCP and safe-outputs are enabled")
+ })
+
+ t.Run("only GitHub MCP enabled (no safe-outputs)", func(t *testing.T) {
+ compiler := &Compiler{}
+
+ data := &WorkflowData{
+ ParsedTools: NewTools(map[string]any{"github": true}),
+ SafeOutputs: nil,
+ }
+
+ sections := compiler.collectPromptSections(data)
+ require.NotEmpty(t, sections, "Should collect sections")
+
+ // The base GitHub MCP guidance file must be selected (without safeoutputs)
+ var githubMCPSection *PromptSection
+ for i := range sections {
+ if sections[i].IsFile && strings.Contains(sections[i].Content, "github_mcp_tools") {
+ githubMCPSection = §ions[i]
+ break
+ }
+ }
+ require.NotNil(t, githubMCPSection, "Should include github_mcp_tools file even without safe-outputs")
+ assert.Equal(t, githubMCPToolsPromptFile, githubMCPSection.Content,
+ "Should use the base variant when only GitHub MCP is enabled")
+ })
+
+ t.Run("no GitHub MCP tool", func(t *testing.T) {
+ compiler := &Compiler{}
+
+ data := &WorkflowData{
+ ParsedTools: NewTools(map[string]any{}),
+ SafeOutputs: &SafeOutputsConfig{
+ MissingData: &MissingDataConfig{},
+ NoOp: &NoOpConfig{},
+ },
+ }
+
+ sections := compiler.collectPromptSections(data)
+
+ // Without GitHub MCP there should be no github_mcp_tools file section
+ for _, section := range sections {
+ if section.IsFile {
+ assert.NotContains(t, section.Content, "github_mcp_tools",
+ "Should not include GitHub MCP guidance when GitHub tool is not enabled")
+ }
+ }
+ })
+}
diff --git a/pkg/workflow/validation_helpers.go b/pkg/workflow/validation_helpers.go
index 65b22f61db5..01aeb75d825 100644
--- a/pkg/workflow/validation_helpers.go
+++ b/pkg/workflow/validation_helpers.go
@@ -9,6 +9,7 @@
// - newValidationLogger() - Creates a standardized logger for a validation domain
// - validateIntRange() - Validates that an integer value is within a specified range
// - validateMountStringFormat() - Parses and validates a "source:dest:mode" mount string
+// - containsTrigger() - Reports whether an 'on:' section includes a named trigger
//
// # Design Rationale
//
@@ -150,3 +151,25 @@ func validateNoDuplicateIDs[T any](items []T, idFunc func(T) string, onDuplicate
}
return nil
}
+
+// containsTrigger reports whether the given 'on:' section value includes
+// the named trigger. It handles the three GitHub Actions forms:
+// - string: "on: "
+// - []any: "on: [push, ]"
+// - map[string]any: "on:\n : ..."
+func containsTrigger(onSection any, triggerName string) bool {
+ switch on := onSection.(type) {
+ case string:
+ return on == triggerName
+ case []any:
+ for _, trigger := range on {
+ if triggerStr, ok := trigger.(string); ok && triggerStr == triggerName {
+ return true
+ }
+ }
+ case map[string]any:
+ _, ok := on[triggerName]
+ return ok
+ }
+ return false
+}
diff --git a/pkg/workflow/validation_helpers_test.go b/pkg/workflow/validation_helpers_test.go
index c4885e23b2e..86e2ca00403 100644
--- a/pkg/workflow/validation_helpers_test.go
+++ b/pkg/workflow/validation_helpers_test.go
@@ -420,3 +420,124 @@ func TestValidateMountStringFormat(t *testing.T) {
})
}
}
+
+// TestContainsTrigger tests the shared trigger detection helper.
+func TestContainsTrigger(t *testing.T) {
+ tests := []struct {
+ name string
+ onSection any
+ triggerName string
+ expected bool
+ }{
+ // string form
+ {
+ name: "string: matching trigger",
+ onSection: "workflow_dispatch",
+ triggerName: "workflow_dispatch",
+ expected: true,
+ },
+ {
+ name: "string: non-matching trigger",
+ onSection: "push",
+ triggerName: "workflow_dispatch",
+ expected: false,
+ },
+ {
+ name: "string: partial match does not count",
+ onSection: "workflow_dispatch_extra",
+ triggerName: "workflow_dispatch",
+ expected: false,
+ },
+ // []any form
+ {
+ name: "slice: contains trigger",
+ onSection: []any{"push", "workflow_dispatch"},
+ triggerName: "workflow_dispatch",
+ expected: true,
+ },
+ {
+ name: "slice: trigger absent",
+ onSection: []any{"push", "pull_request"},
+ triggerName: "workflow_dispatch",
+ expected: false,
+ },
+ {
+ name: "slice: single element matching",
+ onSection: []any{"workflow_call"},
+ triggerName: "workflow_call",
+ expected: true,
+ },
+ {
+ name: "slice: non-string element skipped",
+ onSection: []any{42, "workflow_dispatch"},
+ triggerName: "workflow_dispatch",
+ expected: true,
+ },
+ {
+ name: "slice: empty slice",
+ onSection: []any{},
+ triggerName: "workflow_dispatch",
+ expected: false,
+ },
+ // map form
+ {
+ name: "map: trigger key present",
+ onSection: map[string]any{"workflow_dispatch": nil},
+ triggerName: "workflow_dispatch",
+ expected: true,
+ },
+ {
+ name: "map: trigger key present with value",
+ onSection: map[string]any{"workflow_dispatch": map[string]any{"inputs": nil}},
+ triggerName: "workflow_dispatch",
+ expected: true,
+ },
+ {
+ name: "map: trigger key absent",
+ onSection: map[string]any{"push": nil, "pull_request": nil},
+ triggerName: "workflow_dispatch",
+ expected: false,
+ },
+ {
+ name: "map: empty map",
+ onSection: map[string]any{},
+ triggerName: "workflow_dispatch",
+ expected: false,
+ },
+ // nil / unsupported types
+ {
+ name: "nil onSection",
+ onSection: nil,
+ triggerName: "workflow_dispatch",
+ expected: false,
+ },
+ {
+ name: "unsupported type",
+ onSection: 42,
+ triggerName: "workflow_dispatch",
+ expected: false,
+ },
+ // workflow_call variant (ensures triggerName is respected)
+ {
+ name: "string: workflow_call matching",
+ onSection: "workflow_call",
+ triggerName: "workflow_call",
+ expected: true,
+ },
+ {
+ name: "string: workflow_dispatch does not match workflow_call",
+ onSection: "workflow_dispatch",
+ triggerName: "workflow_call",
+ expected: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := containsTrigger(tt.onSection, tt.triggerName)
+ if result != tt.expected {
+ t.Errorf("containsTrigger(%v, %q) = %v, want %v", tt.onSection, tt.triggerName, result, tt.expected)
+ }
+ })
+ }
+}
diff --git a/pkg/workflow/yaml.go b/pkg/workflow/yaml.go
index 08ececb14f2..49a3c2b5c6e 100644
--- a/pkg/workflow/yaml.go
+++ b/pkg/workflow/yaml.go
@@ -84,9 +84,11 @@
package workflow
import (
+ "fmt"
"regexp"
"slices"
"sort"
+ "strconv"
"strings"
"github.com/github/gh-aw/pkg/logger"
@@ -306,3 +308,53 @@ func CleanYAMLNullValues(yamlStr string) string {
return strings.Join(lines, "\n")
}
+
+// formatYAMLValue formats a value for YAML output, quoting strings and rendering
+// booleans/numbers as unquoted scalars.
+func formatYAMLValue(value any) string {
+ switch v := value.(type) {
+ case string:
+ // Quote strings if they contain special characters or look like non-string types
+ if v == "true" || v == "false" || v == "null" {
+ return fmt.Sprintf("'%s'", v)
+ }
+ // Check if it's a number
+ if _, err := fmt.Sscanf(v, "%f", new(float64)); err == nil {
+ return fmt.Sprintf("'%s'", v)
+ }
+ // Return as-is for simple strings, quote for complex ones
+ return fmt.Sprintf("'%s'", v)
+ case bool:
+ if v {
+ return "true"
+ }
+ return "false"
+ case int:
+ return strconv.Itoa(v)
+ case int8:
+ return strconv.Itoa(int(v))
+ case int16:
+ return strconv.Itoa(int(v))
+ case int32:
+ return strconv.Itoa(int(v))
+ case int64:
+ return strconv.FormatInt(v, 10)
+ case uint:
+ return strconv.FormatUint(uint64(v), 10)
+ case uint8:
+ return strconv.FormatUint(uint64(v), 10)
+ case uint16:
+ return strconv.FormatUint(uint64(v), 10)
+ case uint32:
+ return strconv.FormatUint(uint64(v), 10)
+ case uint64:
+ return strconv.FormatUint(v, 10)
+ case float32:
+ return fmt.Sprintf("%v", v)
+ case float64:
+ return fmt.Sprintf("%v", v)
+ default:
+ // For other types, convert to string and quote
+ return fmt.Sprintf("'%v'", v)
+ }
+}
diff --git a/pkg/workflow/yaml_test.go b/pkg/workflow/yaml_test.go
index 7da321f4759..622f404bf3c 100644
--- a/pkg/workflow/yaml_test.go
+++ b/pkg/workflow/yaml_test.go
@@ -406,3 +406,52 @@ func TestExtractTopLevelYAMLSectionWithOrdering(t *testing.T) {
})
}
}
+
+func TestFormatYAMLValue(t *testing.T) {
+ tests := []struct {
+ name string
+ value any
+ expected string
+ }{
+ // string cases
+ {name: "string: true keyword quoted", value: "true", expected: "'true'"},
+ {name: "string: false keyword quoted", value: "false", expected: "'false'"},
+ {name: "string: null keyword quoted", value: "null", expected: "'null'"},
+ {name: "string: numeric string quoted", value: "42", expected: "'42'"},
+ {name: "string: float string quoted", value: "3.14", expected: "'3.14'"},
+ {name: "string: plain string quoted", value: "hello", expected: "'hello'"},
+ {name: "string: empty string quoted", value: "", expected: "''"},
+ // bool cases
+ {name: "bool: true", value: true, expected: "true"},
+ {name: "bool: false", value: false, expected: "false"},
+ // integer cases
+ {name: "int", value: int(42), expected: "42"},
+ {name: "int8", value: int8(8), expected: "8"},
+ {name: "int16", value: int16(16), expected: "16"},
+ {name: "int32", value: int32(32), expected: "32"},
+ {name: "int64", value: int64(64), expected: "64"},
+ {name: "int: zero", value: int(0), expected: "0"},
+ {name: "int: negative", value: int(-1), expected: "-1"},
+ // unsigned integer cases
+ {name: "uint", value: uint(10), expected: "10"},
+ {name: "uint8", value: uint8(8), expected: "8"},
+ {name: "uint16", value: uint16(16), expected: "16"},
+ {name: "uint32", value: uint32(32), expected: "32"},
+ {name: "uint64", value: uint64(64), expected: "64"},
+ // float cases
+ {name: "float32", value: float32(1.5), expected: "1.5"},
+ {name: "float64", value: float64(2.5), expected: "2.5"},
+ // default fallback
+ {name: "nil: quoted", value: nil, expected: "''"},
+ {name: "struct: quoted", value: struct{ A int }{A: 1}, expected: "'{1}'"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := formatYAMLValue(tt.value)
+ if result != tt.expected {
+ t.Errorf("formatYAMLValue(%v) = %q, want %q", tt.value, result, tt.expected)
+ }
+ })
+ }
+}
diff --git a/scratchpad/layout.md b/scratchpad/layout.md
index dfbedc5bc0f..399f4b3eb52 100644
--- a/scratchpad/layout.md
+++ b/scratchpad/layout.md
@@ -1,17 +1,17 @@
# GitHub Actions Workflow Layout Specification
> Auto-generated specification documenting patterns used in compiled `.lock.yml` files.
-> Last updated: 2026-03-09
+> Last updated: 2026-03-16
## Overview
This document catalogs all file paths, folder names, artifact names, and other patterns used across our compiled GitHub Actions workflows (`.lock.yml` files). It serves as a comprehensive reference for developers working with the gh-aw codebase.
**Statistics:**
-- **Lock files analyzed**: 166
-- **Unique GitHub Actions**: 22
-- **Artifact patterns**: 20
-- **Job name patterns**: 22
+- **Lock files analyzed**: 172
+- **Unique GitHub Actions**: 23
+- **Artifact patterns**: 22
+- **Job name patterns**: 25
- **File path references**: 37
## GitHub Actions
@@ -22,26 +22,27 @@ Common GitHub Actions used across compiled workflows:
|--------|---------------|-------------|---------|
| `actions/checkout` | `de0fac2...` | Checks out repository code | Used in almost all workflows for accessing repo content |
| `actions/upload-artifact` | `bbbca2d...` | Uploads build artifacts | Used for agent outputs, patches, prompts, logs, and safe-output data |
-| `actions/download-artifact` | `70fc10c...` | Downloads artifacts from previous jobs | Used in safe-output jobs and conclusion jobs |
-| `actions/setup-node` | `6044e13...` | Sets up Node.js environment | Used in workflows requiring npm/node |
+| `actions/download-artifact` | `3e5f45b2...` | Downloads artifacts from previous jobs | Used in safe-output jobs and conclusion jobs |
+| `actions/setup-node` | `53b83947...` | Sets up Node.js environment | Used in workflows requiring npm/node |
| `actions/setup-python` | `a309ff8...` | Sets up Python environment | Used for Python-based workflows and scripts |
| `actions/setup-go` | `4b73464...` | Sets up Go environment | Used for Go-based builds and tests |
| `actions/setup-java` | `be666c2...` | Sets up Java environment | Used for Java-based workflows |
-| `actions/setup-dotnet` | `baa11fb...` | Sets up .NET environment | Used for .NET-based workflows |
+| `actions/setup-dotnet` | `c2fa09f...` | Sets up .NET environment | Used for .NET-based workflows |
| `actions/github-script` | `ed59741...` | Runs GitHub API scripts | Used for GitHub API interactions and workflow logic |
| `actions/cache` | `cdf6c1f...` | Caches dependencies | Used for caching npm, pip, go modules |
| `actions/cache/restore` | `cdf6c1f...` | Restores cached dependencies | Explicit cache restore action |
| `actions/cache/save` | `cdf6c1f...` | Saves dependencies to cache | Explicit cache save action |
-| `docker/setup-buildx-action` | `8d2750c...` | Sets up Docker Buildx | Used for multi-platform Docker builds |
-| `docker/build-push-action` | `10e90e3...` | Builds and pushes Docker images | Used in release workflows |
-| `docker/login-action` | `c94ce9f...` | Logs in to Docker registry | Used before pushing Docker images |
-| `docker/metadata-action` | `c299e40...` | Extracts Docker metadata | Used for tagging Docker images |
-| `astral-sh/setup-uv` | `5a095e7...`, `eac588ad...` | Sets up uv package manager | Used for Python package management |
-| `anchore/sbom-action` | `17ae174...` | Generates SBOM | Used for security and compliance |
+| `docker/setup-buildx-action` | `4d04d5d9...` | Sets up Docker Buildx | Used for multi-platform Docker builds |
+| `docker/build-push-action` | `d08e5c35...` | Builds and pushes Docker images | Used in release workflows |
+| `docker/login-action` | `b45d80f8...` | Logs in to Docker registry | Used before pushing Docker images |
+| `docker/metadata-action` | `030e8812...` | Extracts Docker metadata | Used for tagging Docker images |
+| `astral-sh/setup-uv` | `e06108dd...`, `eac588ad...` | Sets up uv package manager | Used for Python package management |
+| `anchore/sbom-action` | `57aae528...` | Generates SBOM | Used for security and compliance |
| `super-linter/super-linter` | `61abc07...` | Runs super-linter | Used for code quality checks |
-| `github/stale-repos` | `86c425f...` | Manages stale repositories | Used for repository maintenance |
+| `github/stale-repos` | `f592689f...` | Manages stale repositories | Used for repository maintenance |
+| `microsoft/apm-action` | `5eac264...` | Collects APM bundle data | Used for performance monitoring and APM artifact creation |
| `./actions/setup` | N/A (local) | Custom setup action | Copies JavaScript and shell scripts to `/tmp/gh-aw/actions` |
-| `github/gh-aw/actions/setup` | `a70c5ea...` | Remote setup action | Same as local `./actions/setup` but referenced remotely |
+| `github/gh-aw-actions/setup` | `c303e453...` | Remote setup action | Same as local `./actions/setup` but referenced remotely from gh-aw-actions repo |
## Artifact Names
@@ -50,9 +51,10 @@ Artifacts uploaded/downloaded between workflow jobs:
| Name | Upload Context | Download Context | Description |
|------|----------------|------------------|-------------|
| `activation` | Activation job | Activation job, conclusion job | Activation job outputs (sanitized text, metadata) |
-| `agent-output` | Agent job | Safe-output jobs, detection job, conclusion job | AI agent execution output (JSON format) |
-| `agent-artifacts` | Agent job | Detection job, conclusion job | Additional artifacts from agent execution |
-| `agent_outputs` | Agent job | Safe-output jobs | Alternative name for agent outputs |
+| `agent` | Agent job | Safe-output jobs, detection job, conclusion job | Unified AI agent artifact (output JSON, patches, prompts, safe-output data) |
+| `apm` | APM job | Conclusion job | APM bundle for performance monitoring (new) |
+| `agent-output` | Agent job (legacy) | Safe-output jobs | Legacy name for agent output; superseded by unified `agent` artifact |
+| `detection` | Detection job | Conclusion job | Detection analysis output (threat logs, analysis results) |
| `safe-output` | Safe-output jobs | Conclusion job | Safe output results (PR creation, commenting, etc.) |
| `safe-output-items` | Safe-output jobs | Conclusion job | Individual safe output item payloads (JSONL format) |
| `safe-outputs-assets` | Safe-output jobs | Conclusion job | Assets generated by safe-output tools |
@@ -61,14 +63,15 @@ Artifacts uploaded/downloaded between workflow jobs:
| `cache-memory-repo-audits` | Agent job | Next workflow run | Repository audit cache data |
| `repo-memory-default` | Agent job | Next workflow run | Default repository memory data |
| `repo-memory-campaigns` | Agent job | Next workflow run | Campaign-specific repository memory |
-| `prompt` | Activation job | Agent job | Rendered prompt file used for agent invocation |
+| `release-binaries-${{ needs.config.outputs.release_tag }}` | Release job | Download step | Release binary artifacts tagged with version |
| `python-source-and-data` | Agent job | Upload assets job | Python scripts and data files |
| `trending-source-and-data` | Agent job | Upload assets job | Trending analysis source and data |
| `trending-charts` | Agent job | Upload assets job | Charts generated from trending data |
| `data-charts` | Agent job | Upload assets job | General data visualization charts |
| `sbom-artifacts` | SBOM job | Download step | Software Bill of Materials artifacts |
| `super-linter-log` | Super-linter job | Debug step | Linter execution logs |
-| `threat-detection.log` | Detection job | Conclusion job | Threat detection analysis logs |
+| `${{ needs.activation.outputs.artifact_prefix }}agent` | Agent job | Downstream jobs | Dynamic-prefixed agent artifact (used in `workflow_call` context) |
+| `${{ needs.activation.outputs.artifact_prefix }}activation` | Activation job | Agent job | Dynamic-prefixed activation artifact (used in `workflow_call` context) |
## Common Job Names
@@ -87,6 +90,7 @@ Standard job names across compiled workflows:
| `precompute` | Pre-computation | `activation` | Performs pre-computation before agent execution |
| `update_cache_memory` | Cache update | `agent` | Updates cache-memory artifacts with new data |
| `push_repo_memory` | Repository memory push | `agent` | Pushes repository memory data to storage |
+| `push_tag` | Git tag push | Various | Pushes a git tag (e.g., for releases or version tracking) |
| `unlock` | Workflow unlock | `agent` | Unlocks resources after agent execution |
| `upload_assets` | Asset upload | Safe-output jobs | Uploads generated assets to GitHub releases or storage |
| `post_to_slack_channel` | Slack notification | Safe-output jobs | Posts workflow results to Slack channel |
@@ -99,6 +103,8 @@ Standard job names across compiled workflows:
| `super_linter` | Code linting | Various | Runs super-linter on codebase |
| `ast_grep` | AST analysis | Various | Runs AST-based code analysis |
| `check_ci_status` | CI status check | Various | Checks CI pipeline status |
+| `call-smoke-workflow-call` | Smoke test caller | Various | Calls the smoke test reusable workflow |
+| `sync_actions` | Actions sync | Various | Syncs actions or configuration across repos |
## File Paths
@@ -395,9 +401,9 @@ This specification is automatically maintained by the **Layout Specification Mai
4. Updates this document with findings
5. Creates a PR with the changes
-**Last extraction run**: 2026-03-09
-**Lock files analyzed**: 166
-**Patterns documented**: 210+
+**Last extraction run**: 2026-03-16
+**Lock files analyzed**: 172
+**Patterns documented**: 220+
---
]