diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml
index 83a7a0ea7..aab421e77 100644
--- a/.github/workflows/smoke-codex.lock.yml
+++ b/.github/workflows/smoke-codex.lock.yml
@@ -13,7 +13,7 @@
# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
#
-# This file was automatically generated by gh-aw (v0.47.0). DO NOT EDIT.
+# This file was automatically generated by gh-aw (v0.58.2). DO NOT EDIT.
#
# To update this file, edit the corresponding .md file and run:
# gh aw compile
@@ -30,7 +30,7 @@
# - shared/mcp/tavily.md
# - shared/reporting.md
#
-# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"1a3519df2a44c468f675d3b8047022c886a1332762a222046de6b2d709e0cd29","compiler_version":"v0.47.0"}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"4e90f52021e1c90e79dd139075b622e6533a4d7555cf9340fa36415e3412c288","compiler_version":"v0.58.2","strict":true}
name: "Smoke Codex"
"on":
@@ -47,14 +47,14 @@ name: "Smoke Codex"
permissions: {}
concurrency:
- group: "gh-aw-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}"
+ group: "gh-aw-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref || github.run_id }}"
cancel-in-progress: true
run-name: "Smoke Codex"
jobs:
activation:
- if: (github.event_name != 'pull_request' || (github.event.pull_request.head.repo.id == github.repository_id && github.event.sender.type != 'Bot'))
+ if: (github.event_name != 'pull_request') || (github.event.pull_request.head.repo.id == github.repository_id)
runs-on: ubuntu-slim
permissions:
contents: read
@@ -65,26 +65,64 @@ jobs:
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: Setup Scripts
- uses: github/gh-aw/actions/setup@v0.47.0
+ uses: github/gh-aw/actions/setup@v0.58.2
with:
destination: /opt/gh-aw/actions
- - name: Validate context variables
+ - name: Generate agentic run info
+ id: generate_aw_info
+ env:
+ GH_AW_INFO_ENGINE_ID: "codex"
+ GH_AW_INFO_ENGINE_NAME: "Codex"
+ GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_CODEX || '' }}
+ GH_AW_INFO_VERSION: ""
+ GH_AW_INFO_AGENT_VERSION: "latest"
+ GH_AW_INFO_CLI_VERSION: "v0.58.2"
+ GH_AW_INFO_WORKFLOW_NAME: "Smoke Codex"
+ GH_AW_INFO_EXPERIMENTAL: "false"
+ GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
+ GH_AW_INFO_STAGED: "false"
+ GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github","playwright"]'
+ 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 { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/validate_context_variables.cjs');
- await main();
+ const { main } = require('/opt/gh-aw/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
+ env:
+ CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Checkout .github and .agents folders
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
- fetch-depth: 1
persist-credentials: false
+ sparse-checkout-cone-mode: true
+ fetch-depth: 1
+ - name: Add hooray reaction for immediate feedback
+ id: react
+ if: github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment' || (github.event_name == 'pull_request') && (github.event.pull_request.head.repo.id == github.repository_id)
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_REACTION: "hooray"
+ 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/add_reaction.cjs');
+ await main();
- name: Check workflow file timestamps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
@@ -118,43 +156,20 @@ jobs:
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
bash /opt/gh-aw/actions/create_prompt_first.sh
- cat << 'GH_AW_PROMPT_EOF' > "$GH_AW_PROMPT"
+ {
+ cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md" >> "$GH_AW_PROMPT"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md" >> "$GH_AW_PROMPT"
- cat "/opt/gh-aw/prompts/markdown.md" >> "$GH_AW_PROMPT"
- cat "/opt/gh-aw/prompts/playwright_prompt.md" >> "$GH_AW_PROMPT"
- cat "/opt/gh-aw/prompts/cache_memory_prompt.md" >> "$GH_AW_PROMPT"
- cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT"
-
- GitHub API Access Instructions
-
- The gh CLI is NOT authenticated. Do NOT use gh commands for GitHub operations.
-
-
- To create or modify GitHub resources (issues, discussions, pull requests, etc.), you MUST call the appropriate safe output tool. Simply writing content will NOT work - the workflow requires actual tool calls.
-
- Temporary IDs: Some safe output tools support a temporary ID field (usually named temporary_id) so you can reference newly-created items elsewhere in the SAME agent output (for example, using #aw_abc1 in a later body).
-
- **IMPORTANT - temporary_id format rules:**
- - If you DON'T need to reference the item later, OMIT the temporary_id field entirely (it will be auto-generated if needed)
- - If you DO need cross-references/chaining, you MUST match this EXACT validation regex: /^aw_[A-Za-z0-9]{3,8}$/i
- - Format: aw_ prefix followed by 3 to 8 alphanumeric characters (A-Z, a-z, 0-9, case-insensitive)
- - Valid alphanumeric characters: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
- - INVALID examples: aw_ab (too short), aw_123456789 (too long), aw_test-id (contains hyphen), aw_id_123 (contains underscore)
- - VALID examples: aw_abc, aw_abc1, aw_Test123, aw_A1B2C3D4, aw_12345678
- - To generate valid IDs: use 3-8 random alphanumeric characters or omit the field to let the system auto-generate
-
- Do NOT invent other aw_* formats — downstream steps will reject them with validation errors matching against /^aw_[A-Za-z0-9]{3,8}$/i.
-
- Discover available tools from the safeoutputs MCP server.
-
- **Critical**: Tool calls write structured data that downstream jobs process. Without tool calls, follow-up actions will be skipped.
-
- **Note**: If you made no other safe output tool calls during this workflow execution, call the "noop" tool to provide a status message indicating completion or that no actions were needed.
-
-
+ 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_PROMPT_EOF'
+
+ Tools: add_comment, create_issue, add_labels, hide_comment, missing_tool, missing_data, noop
+
The following GitHub context information is available for this workflow:
{{#if __GH_AW_GITHUB_ACTOR__ }}
@@ -184,29 +199,29 @@ jobs:
GH_AW_PROMPT_EOF
- cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT"
+ cat << 'GH_AW_PROMPT_EOF'
GH_AW_PROMPT_EOF
- cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT"
+ cat << 'GH_AW_PROMPT_EOF'
{{#runtime-import .github/workflows/shared/gh.md}}
GH_AW_PROMPT_EOF
- cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT"
+ cat << 'GH_AW_PROMPT_EOF'
{{#runtime-import .github/workflows/shared/mcp/tavily.md}}
GH_AW_PROMPT_EOF
- cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT"
+ cat << 'GH_AW_PROMPT_EOF'
{{#runtime-import .github/workflows/shared/reporting.md}}
GH_AW_PROMPT_EOF
- cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT"
+ cat << 'GH_AW_PROMPT_EOF'
{{#runtime-import .github/workflows/shared/github-queries-safe-input.md}}
GH_AW_PROMPT_EOF
- cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT"
+ cat << 'GH_AW_PROMPT_EOF'
{{#runtime-import .github/workflows/smoke-codex.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
- GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
with:
script: |
@@ -229,8 +244,6 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
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_PRE_ACTIVATION_OUTPUTS_MATCHED_COMMAND: ${{ needs.pre_activation.outputs.matched_command }}
with:
script: |
const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
@@ -252,9 +265,7 @@ jobs:
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,
- GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED,
- GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_MATCHED_COMMAND: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_MATCHED_COMMAND
+ GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE
}
});
- name: Validate prompt placeholders
@@ -265,12 +276,14 @@ jobs:
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
run: bash /opt/gh-aw/actions/print_prompt_summary.sh
- - name: Upload prompt artifact
+ - name: Upload activation artifact
if: success()
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
- name: prompt
- path: /tmp/gh-aw/aw-prompts/prompt.txt
+ name: activation
+ path: |
+ /tmp/gh-aw/aw_info.json
+ /tmp/gh-aw/aw-prompts/prompt.txt
retention-days: 1
agent:
@@ -293,14 +306,15 @@ jobs:
GH_AW_WORKFLOW_ID_SANITIZED: smokecodex
outputs:
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
+ detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
+ detection_success: ${{ steps.detection_conclusion.outputs.success }}
has_patch: ${{ steps.collect_output.outputs.has_patch }}
- model: ${{ steps.generate_aw_info.outputs.model }}
+ model: ${{ needs.activation.outputs.model }}
output: ${{ steps.collect_output.outputs.output }}
output_types: ${{ steps.collect_output.outputs.output_types }}
- secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
steps:
- name: Setup Scripts
- uses: github/gh-aw/actions/setup@v0.47.0
+ uses: github/gh-aw/actions/setup@v0.58.2
with:
destination: /opt/gh-aw/actions
- name: Checkout repository
@@ -313,7 +327,7 @@ jobs:
- name: Create cache-memory directory
run: bash /opt/gh-aw/actions/create_cache_memory_dir.sh
- name: Restore cache-memory file share data
- uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
+ uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
key: memory-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
path: /tmp/gh-aw/cache-memory
@@ -326,6 +340,7 @@ jobs:
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"
@@ -333,7 +348,7 @@ jobs:
- name: Checkout PR branch
id: checkout-pr
if: |
- github.event.pull_request
+ (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 }}
@@ -344,63 +359,13 @@ jobs:
setupGlobals(core, github, context, exec, io);
const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
await main();
- - name: Generate agentic run info
- id: generate_aw_info
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- with:
- script: |
- const fs = require('fs');
-
- const awInfo = {
- engine_id: "codex",
- engine_name: "Codex",
- model: process.env.GH_AW_MODEL_AGENT_CODEX || "",
- version: "",
- agent_version: "0.104.0",
- cli_version: "v0.47.0",
- workflow_name: "Smoke Codex",
- experimental: false,
- supports_tools_allowlist: true,
- run_id: context.runId,
- run_number: context.runNumber,
- run_attempt: process.env.GITHUB_RUN_ATTEMPT,
- repository: context.repo.owner + '/' + context.repo.repo,
- ref: context.ref,
- sha: context.sha,
- actor: context.actor,
- event_name: context.eventName,
- staged: false,
- allowed_domains: ["defaults","github","playwright"],
- firewall_enabled: true,
- awf_version: "v0.20.1",
- awmg_version: "v0.1.4",
- steps: {
- firewall: "squid"
- },
- created_at: new Date().toISOString()
- };
-
- // Write to /tmp/gh-aw directory to avoid inclusion in PR
- const tmpPath = '/tmp/gh-aw/aw_info.json';
- fs.writeFileSync(tmpPath, JSON.stringify(awInfo, null, 2));
- console.log('Generated aw_info.json at:', tmpPath);
- console.log(JSON.stringify(awInfo, null, 2));
-
- // Set model as output for reuse in other steps/jobs
- core.setOutput('model', awInfo.model);
- - 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
- env:
- CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
- OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Setup Node.js
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
+ uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: '24'
package-manager-cache: false
- name: Install Codex
- run: npm install -g --silent @openai/codex@0.104.0
+ run: npm install -g @openai/codex@latest
- name: Install awf dependencies
run: npm ci
- name: Build awf
@@ -437,7 +402,7 @@ jobs:
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.20.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.20.1 ghcr.io/github/gh-aw-firewall/squid:0.20.1 ghcr.io/github/gh-aw-mcpg:v0.1.4 ghcr.io/github/github-mcp-server:v0.30.3 mcr.microsoft.com/playwright/mcp node:lts-alpine
+ 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
- name: Write Safe Outputs Config
run: |
mkdir -p /opt/gh-aw/safeoutputs
@@ -446,6 +411,8 @@ jobs:
cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
{"add_comment":{"max":2},"add_labels":{"allowed":["smoke-codex"],"max":3},"create_issue":{"expires":2,"max":1},"hide_comment":{"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'
[
{
@@ -457,6 +424,10 @@ jobs:
"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": {
@@ -471,9 +442,13 @@ jobs:
"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 8 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,8}$",
+ "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": {
@@ -498,9 +473,25 @@ jobs:
"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). If omitted, the tool will attempt to resolve the target from the current workflow context (triggering issue, PR, or discussion).",
- "type": "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": [
@@ -511,12 +502,16 @@ jobs:
"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: [smoke-codex].",
+ "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: [\"smoke-codex\"].",
"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 item that triggered this workflow.",
+ "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": {
@@ -525,6 +520,10 @@ jobs:
"type": "string"
},
"type": "array"
+ },
+ "secrecy": {
+ "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
+ "type": "string"
}
},
"type": "object"
@@ -540,10 +539,18 @@ jobs:
"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"
@@ -561,9 +568,17 @@ jobs:
"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": [
@@ -582,6 +597,10 @@ jobs:
"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": [
@@ -592,6 +611,10 @@ jobs:
"RESOLVED"
],
"type": "string"
+ },
+ "secrecy": {
+ "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
+ "type": "string"
}
},
"required": [
@@ -618,9 +641,17 @@ jobs:
"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": [],
@@ -643,6 +674,10 @@ jobs:
},
"item_number": {
"issueOrPRNumber": true
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
}
}
},
@@ -658,6 +693,10 @@ jobs:
"itemType": "string",
"itemSanitize": true,
"itemMaxLength": 128
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
}
}
},
@@ -694,6 +733,55 @@ jobs:
}
}
},
+ "hide_comment": {
+ "defaultMax": 5,
+ "fields": {
+ "comment_id": {
+ "required": true,
+ "type": "string",
+ "maxLength": 256
+ },
+ "reason": {
+ "type": "string",
+ "enum": [
+ "SPAM",
+ "ABUSE",
+ "OFF_TOPIC",
+ "OUTDATED",
+ "RESOLVED"
+ ]
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
+ }
+ }
+ },
+ "missing_data": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "context": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "data_type": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ },
+ "reason": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ }
+ }
+ },
"missing_tool": {
"defaultMax": 20,
"fields": {
@@ -766,516 +854,19 @@ jobs:
bash /opt/gh-aw/actions/start_safe_outputs_server.sh
- - name: Setup Safe Inputs Config
- run: |
- mkdir -p /opt/gh-aw/safe-inputs/logs
- cat > /opt/gh-aw/safe-inputs/tools.json << 'GH_AW_SAFE_INPUTS_TOOLS_EOF'
- {
- "serverName": "safeinputs",
- "version": "1.0.0",
- "logDir": "/opt/gh-aw/safe-inputs/logs",
- "tools": [
- {
- "name": "gh",
- "description": "Execute any gh CLI command. This tool is accessible as 'safeinputs-gh'. Provide the full command after 'gh' (e.g., args: 'pr list --limit 5'). The tool will run: gh \u003cargs\u003e. Use single quotes ' for complex args to avoid shell interpretation issues.",
- "inputSchema": {
- "properties": {
- "args": {
- "description": "Arguments to pass to gh CLI (without the 'gh' prefix). Examples: 'pr list --limit 5', 'issue view 123', 'api repos/{owner}/{repo}'",
- "type": "string"
- }
- },
- "required": [
- "args"
- ],
- "type": "object"
- },
- "handler": "gh.sh",
- "env": {
- "GH_AW_GH_TOKEN": "GH_AW_GH_TOKEN",
- "GH_DEBUG": "GH_DEBUG"
- },
- "timeout": 60
- },
- {
- "name": "github-discussion-query",
- "description": "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.",
- "inputSchema": {
- "properties": {
- "jq": {
- "description": "jq filter expression to apply to output. If not provided, returns schema info instead of full data.",
- "type": "string"
- },
- "limit": {
- "description": "Maximum number of discussions to fetch (default: 30)",
- "type": "number"
- },
- "repo": {
- "description": "Repository in owner/repo format (defaults to current repository)",
- "type": "string"
- }
- },
- "type": "object"
- },
- "handler": "github-discussion-query.sh",
- "env": {
- "GH_TOKEN": "GH_TOKEN"
- },
- "timeout": 60
- },
- {
- "name": "github-issue-query",
- "description": "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.",
- "inputSchema": {
- "properties": {
- "jq": {
- "description": "jq filter expression to apply to output. If not provided, returns schema info instead of full data.",
- "type": "string"
- },
- "limit": {
- "description": "Maximum number of issues to fetch (default: 30)",
- "type": "number"
- },
- "repo": {
- "description": "Repository in owner/repo format (defaults to current repository)",
- "type": "string"
- },
- "state": {
- "description": "Issue state: open, closed, all (default: open)",
- "type": "string"
- }
- },
- "type": "object"
- },
- "handler": "github-issue-query.sh",
- "env": {
- "GH_TOKEN": "GH_TOKEN"
- },
- "timeout": 60
- },
- {
- "name": "github-pr-query",
- "description": "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.",
- "inputSchema": {
- "properties": {
- "jq": {
- "description": "jq filter expression to apply to output. If not provided, returns schema info instead of full data.",
- "type": "string"
- },
- "limit": {
- "description": "Maximum number of PRs to fetch (default: 30)",
- "type": "number"
- },
- "repo": {
- "description": "Repository in owner/repo format (defaults to current repository)",
- "type": "string"
- },
- "state": {
- "description": "PR state: open, closed, merged, all (default: open)",
- "type": "string"
- }
- },
- "type": "object"
- },
- "handler": "github-pr-query.sh",
- "env": {
- "GH_TOKEN": "GH_TOKEN"
- },
- "timeout": 60
- }
- ]
- }
- GH_AW_SAFE_INPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safe-inputs/mcp-server.cjs << 'GH_AW_SAFE_INPUTS_SERVER_EOF'
- const path = require("path");
- const { startHttpServer } = require("./safe_inputs_mcp_server_http.cjs");
- const configPath = path.join(__dirname, "tools.json");
- const port = parseInt(process.env.GH_AW_SAFE_INPUTS_PORT || "3000", 10);
- const apiKey = process.env.GH_AW_SAFE_INPUTS_API_KEY || "";
- startHttpServer(configPath, {
- port: port,
- stateless: true,
- logDir: "/opt/gh-aw/safe-inputs/logs"
- }).catch(error => {
- console.error("Failed to start safe-inputs HTTP server:", error);
- process.exit(1);
- });
- GH_AW_SAFE_INPUTS_SERVER_EOF
- chmod +x /opt/gh-aw/safe-inputs/mcp-server.cjs
-
- - name: Setup Safe Inputs Tool Files
- run: |
- cat > /opt/gh-aw/safe-inputs/gh.sh << 'GH_AW_SAFE_INPUTS_SH_GH_EOF'
- #!/bin/bash
- # Auto-generated safe-input tool: gh
- # Execute any gh CLI command. This tool is accessible as 'safeinputs-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.
-
- set -euo pipefail
-
- echo "gh $INPUT_ARGS"
- echo " token: ${GH_AW_GH_TOKEN:0:6}..."
- GH_TOKEN="$GH_AW_GH_TOKEN" gh $INPUT_ARGS
-
- GH_AW_SAFE_INPUTS_SH_GH_EOF
- chmod +x /opt/gh-aw/safe-inputs/gh.sh
- cat > /opt/gh-aw/safe-inputs/github-discussion-query.sh << 'GH_AW_SAFE_INPUTS_SH_GITHUB-DISCUSSION-QUERY_EOF'
- #!/bin/bash
- # Auto-generated safe-input 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.
-
- set -euo pipefail
-
- set -e
-
- # Default values
- REPO="${INPUT_REPO:-}"
- LIMIT="${INPUT_LIMIT:-30}"
- JQ_FILTER="${INPUT_JQ:-}"
-
- # Parse repository owner and name
- if [[ -n "$REPO" ]]; then
- OWNER=$(echo "$REPO" | cut -d'/' -f1)
- NAME=$(echo "$REPO" | cut -d'/' -f2)
- else
- # Get current repository from GitHub context
- OWNER="${GITHUB_REPOSITORY_OWNER:-}"
- NAME=$(echo "${GITHUB_REPOSITORY:-}" | cut -d'/' -f2)
- fi
-
- # Validate owner and name
- if [[ -z "$OWNER" || -z "$NAME" ]]; then
- echo "Error: Could not determine repository owner and name" >&2
- exit 1
- fi
-
- # Build GraphQL query for discussions
- GRAPHQL_QUERY=$(cat < /opt/gh-aw/safe-inputs/github-issue-query.sh << 'GH_AW_SAFE_INPUTS_SH_GITHUB-ISSUE-QUERY_EOF'
- #!/bin/bash
- # Auto-generated safe-input 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.
-
- set -euo pipefail
-
- set -e
-
- # Default values
- REPO="${INPUT_REPO:-}"
- STATE="${INPUT_STATE:-open}"
- LIMIT="${INPUT_LIMIT:-30}"
- JQ_FILTER="${INPUT_JQ:-}"
-
- # JSON fields to fetch
- JSON_FIELDS="number,title,state,author,createdAt,updatedAt,closedAt,body,labels,assignees,comments,milestone,url"
-
- # Build and execute gh command
- if [[ -n "$REPO" ]]; then
- OUTPUT=$(gh issue list --state "$STATE" --limit "$LIMIT" --json "$JSON_FIELDS" --repo "$REPO")
- else
- OUTPUT=$(gh issue list --state "$STATE" --limit "$LIMIT" --json "$JSON_FIELDS")
- fi
-
- # Apply jq filter if specified
- if [[ -n "$JQ_FILTER" ]]; then
- jq "$JQ_FILTER" <<< "$OUTPUT"
- else
- # Return schema and size instead of full data
- ITEM_COUNT=$(jq 'length' <<< "$OUTPUT")
- DATA_SIZE=${#OUTPUT}
-
- # Validate values are numeric
- if ! [[ "$ITEM_COUNT" =~ ^[0-9]+$ ]]; then
- ITEM_COUNT=0
- fi
- if ! [[ "$DATA_SIZE" =~ ^[0-9]+$ ]]; then
- DATA_SIZE=0
- fi
-
- cat << EOF
- {
- "message": "No --jq filter provided. Use --jq to filter and retrieve data.",
- "item_count": $ITEM_COUNT,
- "data_size_bytes": $DATA_SIZE,
- "schema": {
- "type": "array",
- "description": "Array of issue objects",
- "item_fields": {
- "number": "integer - Issue number",
- "title": "string - Issue title",
- "state": "string - Issue state (OPEN, CLOSED)",
- "author": "object - Author info with login field",
- "createdAt": "string - ISO timestamp of creation",
- "updatedAt": "string - ISO timestamp of last update",
- "closedAt": "string|null - ISO timestamp of close",
- "body": "string - Issue body content",
- "labels": "array - Array of label objects with name field",
- "assignees": "array - Array of assignee objects with login field",
- "comments": "object - Comments info with totalCount field",
- "milestone": "object|null - Milestone info with title field",
- "url": "string - Issue URL"
- }
- },
- "suggested_queries": [
- {"description": "Get all data", "query": "."},
- {"description": "Get issue numbers and titles", "query": ".[] | {number, title}"},
- {"description": "Get open issues only", "query": ".[] | select(.state == \"OPEN\")"},
- {"description": "Get issues by author", "query": ".[] | select(.author.login == \"USERNAME\")"},
- {"description": "Get issues with label", "query": ".[] | select(.labels | map(.name) | index(\"bug\"))"},
- {"description": "Get issues with many comments", "query": ".[] | select(.comments.totalCount > 5) | {number, title, comments: .comments.totalCount}"},
- {"description": "Count by state", "query": "group_by(.state) | map({state: .[0].state, count: length})"}
- ]
- }
- EOF
- fi
-
-
- GH_AW_SAFE_INPUTS_SH_GITHUB-ISSUE-QUERY_EOF
- chmod +x /opt/gh-aw/safe-inputs/github-issue-query.sh
- cat > /opt/gh-aw/safe-inputs/github-pr-query.sh << 'GH_AW_SAFE_INPUTS_SH_GITHUB-PR-QUERY_EOF'
- #!/bin/bash
- # Auto-generated safe-input 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.
-
- set -euo pipefail
-
- set -e
-
- # Default values
- REPO="${INPUT_REPO:-}"
- STATE="${INPUT_STATE:-open}"
- LIMIT="${INPUT_LIMIT:-30}"
- JQ_FILTER="${INPUT_JQ:-}"
-
- # JSON fields to fetch
- JSON_FIELDS="number,title,state,author,createdAt,updatedAt,mergedAt,closedAt,headRefName,baseRefName,isDraft,reviewDecision,additions,deletions,changedFiles,labels,assignees,reviewRequests,url"
-
- # Build and execute gh command
- if [[ -n "$REPO" ]]; then
- OUTPUT=$(gh pr list --state "$STATE" --limit "$LIMIT" --json "$JSON_FIELDS" --repo "$REPO")
- else
- OUTPUT=$(gh pr list --state "$STATE" --limit "$LIMIT" --json "$JSON_FIELDS")
- fi
-
- # Apply jq filter if specified
- if [[ -n "$JQ_FILTER" ]]; then
- jq "$JQ_FILTER" <<< "$OUTPUT"
- else
- # Return schema and size instead of full data
- ITEM_COUNT=$(jq 'length' <<< "$OUTPUT")
- DATA_SIZE=${#OUTPUT}
-
- # Validate values are numeric
- if ! [[ "$ITEM_COUNT" =~ ^[0-9]+$ ]]; then
- ITEM_COUNT=0
- fi
- if ! [[ "$DATA_SIZE" =~ ^[0-9]+$ ]]; then
- DATA_SIZE=0
- fi
-
- cat << EOF
- {
- "message": "No --jq filter provided. Use --jq to filter and retrieve data.",
- "item_count": $ITEM_COUNT,
- "data_size_bytes": $DATA_SIZE,
- "schema": {
- "type": "array",
- "description": "Array of pull request objects",
- "item_fields": {
- "number": "integer - PR number",
- "title": "string - PR title",
- "state": "string - PR state (OPEN, CLOSED, MERGED)",
- "author": "object - Author info with login field",
- "createdAt": "string - ISO timestamp of creation",
- "updatedAt": "string - ISO timestamp of last update",
- "mergedAt": "string|null - ISO timestamp of merge",
- "closedAt": "string|null - ISO timestamp of close",
- "headRefName": "string - Source branch name",
- "baseRefName": "string - Target branch name",
- "isDraft": "boolean - Whether PR is a draft",
- "reviewDecision": "string|null - Review decision (APPROVED, CHANGES_REQUESTED, REVIEW_REQUIRED)",
- "additions": "integer - Lines added",
- "deletions": "integer - Lines deleted",
- "changedFiles": "integer - Number of files changed",
- "labels": "array - Array of label objects with name field",
- "assignees": "array - Array of assignee objects with login field",
- "reviewRequests": "array - Array of review request objects",
- "url": "string - PR URL"
- }
- },
- "suggested_queries": [
- {"description": "Get all data", "query": "."},
- {"description": "Get PR numbers and titles", "query": ".[] | {number, title}"},
- {"description": "Get open PRs only", "query": ".[] | select(.state == \"OPEN\")"},
- {"description": "Get merged PRs", "query": ".[] | select(.mergedAt != null)"},
- {"description": "Get PRs by author", "query": ".[] | select(.author.login == \"USERNAME\")"},
- {"description": "Get large PRs", "query": ".[] | select(.changedFiles > 10) | {number, title, changedFiles}"},
- {"description": "Count by state", "query": "group_by(.state) | map({state: .[0].state, count: length})"}
- ]
- }
- EOF
- fi
-
-
- GH_AW_SAFE_INPUTS_SH_GITHUB-PR-QUERY_EOF
- chmod +x /opt/gh-aw/safe-inputs/github-pr-query.sh
-
- - name: Generate Safe Inputs MCP Server Config
- id: safe-inputs-config
- run: |
- # Generate a secure random API key (360 bits of entropy, 40+ chars)
- # Mask immediately to prevent timing vulnerabilities
- API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
- echo "::add-mask::${API_KEY}"
-
- PORT=3000
-
- # Set outputs for next steps
- {
- echo "safe_inputs_api_key=${API_KEY}"
- echo "safe_inputs_port=${PORT}"
- } >> "$GITHUB_OUTPUT"
-
- echo "Safe Inputs MCP server will run on port ${PORT}"
-
- - name: Start Safe Inputs MCP HTTP Server
- id: safe-inputs-start
- env:
- DEBUG: '*'
- GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-config.outputs.safe_inputs_port }}
- GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-config.outputs.safe_inputs_api_key }}
- GH_AW_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GH_DEBUG: 1
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- # Environment variables are set above to prevent template injection
- export DEBUG
- export GH_AW_SAFE_INPUTS_PORT
- export GH_AW_SAFE_INPUTS_API_KEY
-
- bash /opt/gh-aw/actions/start_safe_inputs_server.sh
-
- name: Start MCP Gateway
id: start-mcp-gateway
env:
- GH_AW_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }}
- GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }}
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 }}
- GH_DEBUG: 1
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
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 }}
TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }}
run: |
set -eo pipefail
mkdir -p /tmp/gh-aw/mcp-config
+ mkdir -p /tmp/gh-aw/mcp-logs/playwright
# Export gateway environment variables for MCP config and gateway script
export MCP_GATEWAY_PORT="80"
@@ -1285,10 +876,11 @@ jobs:
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="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 DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -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_INPUTS_PORT -e GH_AW_SAFE_INPUTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e GH_AW_GH_TOKEN -e GH_DEBUG -e GH_TOKEN -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.4'
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -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'
cat > /tmp/gh-aw/mcp-config/config.toml << GH_AW_MCP_CONFIG_EOF
[history]
@@ -1302,9 +894,9 @@ jobs:
user_agent = "smoke-codex"
startup_timeout_sec = 120
tool_timeout_sec = 60
- container = "ghcr.io/github/github-mcp-server:v0.30.3"
- env = { "GITHUB_PERSONAL_ACCESS_TOKEN" = "$GH_AW_GITHUB_TOKEN", "GITHUB_READ_ONLY" = "1", "GITHUB_TOOLSETS" = "context,repos,issues,pull_requests" }
- env_vars = ["GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_READ_ONLY", "GITHUB_TOOLSETS"]
+ container = "ghcr.io/github/github-mcp-server:v0.32.0"
+ env = { "GITHUB_HOST" = "$GITHUB_SERVER_URL", "GITHUB_PERSONAL_ACCESS_TOKEN" = "$GH_AW_GITHUB_TOKEN", "GITHUB_READ_ONLY" = "1", "GITHUB_TOOLSETS" = "context,repos,issues,pull_requests" }
+ env_vars = ["GITHUB_HOST", "GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_READ_ONLY", "GITHUB_TOOLSETS"]
[mcp_servers.playwright]
container = "mcr.microsoft.com/playwright/mcp"
@@ -1318,19 +910,10 @@ jobs:
]
entrypointArgs = [
"--output-dir",
- "/tmp/gh-aw/mcp-logs/playwright",
- "--allowed-hosts",
- "localhost;localhost:*;127.0.0.1;127.0.0.1:*",
- "--allowed-origins",
- "localhost;localhost:*;127.0.0.1;127.0.0.1:*"
+ "/tmp/gh-aw/mcp-logs/playwright"
]
mounts = ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"]
- [mcp_servers.safeinputs]
- type = "http"
- url = "http://host.docker.internal:$GH_AW_SAFE_INPUTS_PORT"
- headers = { Authorization = "$GH_AW_SAFE_INPUTS_API_KEY" }
-
[mcp_servers.safeoutputs]
type = "http"
url = "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT"
@@ -1348,8 +931,9 @@ jobs:
{
"mcpServers": {
"github": {
- "container": "ghcr.io/github/github-mcp-server:v0.30.3",
+ "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",
@@ -1369,20 +953,10 @@ jobs:
"entrypointArgs": [
"--output-dir",
"/tmp/gh-aw/mcp-logs/playwright",
- "--allowed-hosts",
- "localhost,localhost:*,127.0.0.1,127.0.0.1:*,github.com",
- "--allowed-origins",
- "localhost;localhost:*;127.0.0.1;127.0.0.1:*;github.com"
+ "--no-sandbox"
],
"mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"]
},
- "safeinputs": {
- "type": "http",
- "url": "http://host.docker.internal:$GH_AW_SAFE_INPUTS_PORT",
- "headers": {
- "Authorization": "$GH_AW_SAFE_INPUTS_API_KEY"
- }
- },
"safeoutputs": {
"type": "http",
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
@@ -1395,7 +969,10 @@ jobs:
"url": "https://mcp.tavily.com/mcp/",
"headers": {
"Authorization": "Bearer ${{ secrets.TAVILY_API_KEY }}"
- }
+ },
+ "tools": [
+ "*"
+ ]
}
},
"gateway": {
@@ -1406,34 +983,35 @@ jobs:
}
}
GH_AW_MCP_CONFIG_EOF
- - name: Generate workflow overview
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ - name: Download activation artifact
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- script: |
- const { generateWorkflowOverview } = require('/opt/gh-aw/actions/generate_workflow_overview.cjs');
- await generateWorkflowOverview(core);
- - name: Download prompt artifact
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
- with:
- name: prompt
- path: /tmp/gh-aw/aw-prompts
+ name: activation
+ path: /tmp/gh-aw
- name: Clean git credentials
run: bash /opt/gh-aw/actions/clean_git_credentials.sh
- name: Execute Codex
run: |
set -o pipefail
- mkdir -p "$CODEX_HOME/logs"
- 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,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,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,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.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 --build-local --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 --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+ 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,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,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,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.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 --build-local --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 }}
CODEX_HOME: /tmp/gh-aw/mcp-config
GH_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/config.toml
GH_AW_MODEL_AGENT_CODEX: ${{ vars.GH_AW_MODEL_AGENT_CODEX || '' }}
+ GH_AW_PHASE: agent
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_DEBUG: 1
- GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }}
+ GH_AW_VERSION: v0.58.2
+ GITHUB_AW: true
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ 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]
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
@@ -1443,6 +1021,7 @@ jobs:
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"
@@ -1473,20 +1052,21 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
SECRET_TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }}
- - name: Upload Safe Outputs
+ - name: Append agent step summary
if: always()
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
- with:
- name: safe-output
- path: ${{ env.GH_AW_SAFE_OUTPUTS }}
- if-no-files-found: warn
+ run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
+ - name: Copy Safe Outputs
+ if: always()
+ run: |
+ mkdir -p /tmp/gh-aw
+ cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
- name: Ingest agent output
id: collect_output
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_SAFE_OUTPUTS: ${{ 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,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,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,playwright.download.prss.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,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,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,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.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:
@@ -1495,21 +1075,6 @@ jobs:
setupGlobals(core, github, context, exec, io);
const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
await main();
- - name: Upload sanitized agent output
- if: always() && env.GH_AW_AGENT_OUTPUT
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
- with:
- name: agent-output
- path: ${{ env.GH_AW_AGENT_OUTPUT }}
- if-no-files-found: warn
- - name: Upload engine output files
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
- with:
- name: agent_outputs
- path: |
- /tmp/gh-aw/mcp-config/logs/
- /tmp/gh-aw/redacted-urls.log
- if-no-files-found: ignore
- name: Parse agent logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1521,15 +1086,6 @@ jobs:
setupGlobals(core, github, context, exec, io);
const { main } = require('/opt/gh-aw/actions/parse_codex_log.cjs');
await main();
- - name: Parse Safe Inputs 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_safe_inputs_logs.cjs');
- await main();
- name: Parse MCP Gateway logs for step summary
if: always()
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
@@ -1555,7 +1111,7 @@ jobs:
echo 'AWF binary not installed, skipping firewall log summary'
fi
- name: Upload cache-memory data as artifact
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
if: always()
with:
name: cache-memory
@@ -1566,24 +1122,136 @@ jobs:
- name: Upload agent artifacts
if: always()
continue-on-error: true
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
- name: agent-artifacts
+ name: agent
path: |
/tmp/gh-aw/aw-prompts/prompt.txt
- /tmp/gh-aw/aw_info.json
+ /tmp/gh-aw/mcp-config/logs/
+ /tmp/gh-aw/redacted-urls.log
/tmp/gh-aw/mcp-logs/
- /tmp/gh-aw/safe-inputs/logs/
/tmp/gh-aw/sandbox/firewall/logs/
/tmp/gh-aw/agent-stdio.log
/tmp/gh-aw/agent/
+ /tmp/gh-aw/safeoutputs.jsonl
+ /tmp/gh-aw/agent_output.json
+ if-no-files-found: ignore
+ # --- Threat Detection (inline) ---
+ - name: Check if detection needed
+ id: detection_guard
+ if: always()
+ env:
+ OUTPUT_TYPES: ${{ steps.collect_output.outputs.output_types }}
+ HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
+ run: |
+ if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
+ echo "run_detection=true" >> "$GITHUB_OUTPUT"
+ echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH"
+ else
+ echo "run_detection=false" >> "$GITHUB_OUTPUT"
+ echo "Detection skipped: no agent outputs or patches to analyze"
+ fi
+ - name: Clear MCP configuration for detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ rm -f /tmp/gh-aw/mcp-config/mcp-servers.json
+ rm -f /home/runner/.copilot/mcp-config.json
+ rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
+ - name: Prepare threat detection files
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
+ cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
+ cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
+ for f in /tmp/gh-aw/aw-*.patch; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ echo "Prepared threat detection files:"
+ ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ - name: Setup threat detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ WORKFLOW_NAME: "Smoke Codex"
+ WORKFLOW_DESCRIPTION: "Smoke test workflow that validates Codex engine functionality by testing AWF firewall capabilities"
+ HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
+ 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/setup_threat_detection.cjs');
+ await main();
+ - name: Ensure threat-detection directory and log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection
+ touch /tmp/gh-aw/threat-detection/detection.log
+ - name: Execute Codex
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ 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,host.docker.internal,openai.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --build-local --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 }}
+ CODEX_HOME: /tmp/gh-aw/mcp-config
+ GH_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/config.toml
+ GH_AW_MODEL_DETECTION_CODEX: ${{ vars.GH_AW_MODEL_DETECTION_CODEX || '' }}
+ GH_AW_PHASE: detection
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_VERSION: v0.58.2
+ GITHUB_AW: true
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ 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]
+ 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: Parse threat detection results
+ id: parse_detection_results
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ 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_threat_detection_results.cjs');
+ await main();
+ - name: Upload threat detection log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
+ with:
+ name: detection
+ path: /tmp/gh-aw/threat-detection/detection.log
if-no-files-found: ignore
+ - name: Set detection conclusion
+ id: detection_conclusion
+ if: always()
+ env:
+ RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
+ DETECTION_SUCCESS: ${{ steps.parse_detection_results.outputs.success }}
+ run: |
+ if [[ "$RUN_DETECTION" != "true" ]]; then
+ echo "conclusion=skipped" >> "$GITHUB_OUTPUT"
+ echo "success=true" >> "$GITHUB_OUTPUT"
+ echo "Detection was not needed, marking as skipped"
+ elif [[ "$DETECTION_SUCCESS" == "true" ]]; then
+ echo "conclusion=success" >> "$GITHUB_OUTPUT"
+ echo "success=true" >> "$GITHUB_OUTPUT"
+ echo "Detection passed successfully"
+ else
+ echo "conclusion=failure" >> "$GITHUB_OUTPUT"
+ echo "success=false" >> "$GITHUB_OUTPUT"
+ echo "Detection found issues"
+ fi
conclusion:
needs:
- activation
- agent
- - detection
- safe_outputs
- update_cache_memory
if: (always()) && (needs.agent.result != 'skipped')
@@ -1593,32 +1261,37 @@ jobs:
discussions: write
issues: write
pull-requests: write
+ concurrency:
+ group: "gh-aw-conclusion-smoke-codex"
+ cancel-in-progress: false
outputs:
noop_message: ${{ steps.noop.outputs.noop_message }}
tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
total_count: ${{ steps.missing_tool.outputs.total_count }}
steps:
- name: Setup Scripts
- uses: github/gh-aw/actions/setup@v0.47.0
+ uses: github/gh-aw/actions/setup@v0.58.2
with:
destination: /opt/gh-aw/actions
- name: Download agent output artifact
+ id: download-agent-output
continue-on-error: true
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: agent-output
- path: /tmp/gh-aw/safeoutputs/
+ 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/safeoutputs/
- find "/tmp/gh-aw/safeoutputs/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV"
+ 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 No-Op Messages
id: noop
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_NOOP_MAX: 1
+ GH_AW_NOOP_MAX: "1"
GH_AW_WORKFLOW_NAME: "Smoke Codex"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -1649,10 +1322,12 @@ jobs:
GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
GH_AW_WORKFLOW_ID: "smoke-codex"
- GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.agent.outputs.secret_verification_result }}
+ GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }}
GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔮 *The oracle has spoken through [{workflow_name}]({run_url})*\",\"runStarted\":\"🔮 The ancient spirits stir... [{workflow_name}]({run_url}) awakens to divine this {event_type}...\",\"runSuccess\":\"✨ The prophecy is fulfilled... [{workflow_name}]({run_url}) has completed its mystical journey. The stars align. 🌟\",\"runFailure\":\"🌑 The shadows whisper... [{workflow_name}]({run_url}) {status}. The oracle requires further meditation...\"}"
GH_AW_GROUP_REPORTS: "false"
+ GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
+ GH_AW_TIMEOUT_MINUTES: "15"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
@@ -1678,102 +1353,9 @@ jobs:
const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
await main();
- detection:
- needs: agent
- if: needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true'
- runs-on: ubuntu-latest
- permissions: {}
- timeout-minutes: 10
- outputs:
- success: ${{ steps.parse_results.outputs.success }}
- steps:
- - name: Setup Scripts
- uses: github/gh-aw/actions/setup@v0.47.0
- with:
- destination: /opt/gh-aw/actions
- - name: Download agent artifacts
- continue-on-error: true
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
- with:
- name: agent-artifacts
- path: /tmp/gh-aw/threat-detection/
- - name: Download agent output artifact
- continue-on-error: true
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
- with:
- name: agent-output
- path: /tmp/gh-aw/threat-detection/
- - name: Print agent output types
- env:
- AGENT_OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }}
- run: |
- echo "Agent output-types: $AGENT_OUTPUT_TYPES"
- - name: Setup threat detection
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- WORKFLOW_NAME: "Smoke Codex"
- WORKFLOW_DESCRIPTION: "Smoke test workflow that validates Codex engine functionality by testing AWF firewall capabilities"
- HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
- 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/setup_threat_detection.cjs');
- await main();
- - name: Ensure threat-detection directory and log
- run: |
- mkdir -p /tmp/gh-aw/threat-detection
- touch /tmp/gh-aw/threat-detection/detection.log
- - 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
- env:
- CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
- OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- - name: Setup Node.js
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- with:
- node-version: '24'
- package-manager-cache: false
- - name: Install Codex
- run: npm install -g --silent @openai/codex@0.104.0
- - name: Execute Codex
- run: |
- set -o pipefail
- INSTRUCTION="$(cat "$GH_AW_PROMPT")"
- mkdir -p "$CODEX_HOME/logs"
- codex ${GH_AW_MODEL_DETECTION_CODEX:+-c model="$GH_AW_MODEL_DETECTION_CODEX" }exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION" 2>&1 | tee /tmp/gh-aw/threat-detection/detection.log
- env:
- CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
- CODEX_HOME: /tmp/gh-aw/mcp-config
- GH_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/config.toml
- GH_AW_MODEL_DETECTION_CODEX: ${{ vars.GH_AW_MODEL_DETECTION_CODEX || '' }}
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }}
- 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: Parse threat detection results
- id: parse_results
- 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_threat_detection_results.cjs');
- await main();
- - name: Upload threat detection log
- if: always()
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
- with:
- name: threat-detection.log
- path: /tmp/gh-aw/threat-detection/detection.log
- if-no-files-found: ignore
-
safe_outputs:
- needs:
- - agent
- - detection
- if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true')
+ needs: agent
+ if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true')
runs-on: ubuntu-slim
permissions:
contents: read
@@ -1782,36 +1364,48 @@ jobs:
pull-requests: write
timeout-minutes: 15
env:
+ GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/smoke-codex"
GH_AW_ENGINE_ID: "codex"
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔮 *The oracle has spoken through [{workflow_name}]({run_url})*\",\"runStarted\":\"🔮 The ancient spirits stir... [{workflow_name}]({run_url}) awakens to divine this {event_type}...\",\"runSuccess\":\"✨ The prophecy is fulfilled... [{workflow_name}]({run_url}) has completed its mystical journey. The stars align. 🌟\",\"runFailure\":\"🌑 The shadows whisper... [{workflow_name}]({run_url}) {status}. The oracle requires further meditation...\"}"
GH_AW_WORKFLOW_ID: "smoke-codex"
GH_AW_WORKFLOW_NAME: "Smoke Codex"
outputs:
+ code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
+ code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
+ comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }}
+ comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }}
create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
+ created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }}
+ created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }}
process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
steps:
- name: Setup Scripts
- uses: github/gh-aw/actions/setup@v0.47.0
+ uses: github/gh-aw/actions/setup@v0.58.2
with:
destination: /opt/gh-aw/actions
- name: Download agent output artifact
+ id: download-agent-output
continue-on-error: true
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: agent-output
- path: /tmp/gh-aw/safeoutputs/
+ 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/safeoutputs/
- find "/tmp/gh-aw/safeoutputs/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV"
+ 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: "*.githubusercontent.com,172.30.0.1,api.openai.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,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,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.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: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":2},\"add_labels\":{\"allowed\":[\"smoke-codex\"]},\"create_issue\":{\"close_older_issues\":true,\"expires\":2,\"max\":1},\"hide_comment\":{\"max\":5},\"missing_data\":{},\"missing_tool\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -1820,25 +1414,25 @@ jobs:
setupGlobals(core, github, context, exec, io);
const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
await main();
- - name: Upload safe output items manifest
+ - name: Upload Safe Output Items Manifest
if: always()
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: safe-output-items
path: /tmp/safe-output-items.jsonl
if-no-files-found: warn
update_cache_memory:
- needs:
- - agent
- - detection
- if: always() && needs.detection.outputs.success == 'true'
+ needs: agent
+ if: always() && needs.agent.outputs.detection_success == 'true'
runs-on: ubuntu-latest
permissions: {}
+ env:
+ GH_AW_WORKFLOW_ID_SANITIZED: smokecodex
steps:
- name: Download cache-memory artifact (default)
id: download_cache_default
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
continue-on-error: true
with:
name: cache-memory
@@ -1848,13 +1442,13 @@ jobs:
shell: bash
run: |
if [ -d "/tmp/gh-aw/cache-memory" ] && [ "$(ls -A /tmp/gh-aw/cache-memory 2>/dev/null)" ]; then
- echo "has_content=true" >> $GITHUB_OUTPUT
+ echo "has_content=true" >> "$GITHUB_OUTPUT"
else
- echo "has_content=false" >> $GITHUB_OUTPUT
+ echo "has_content=false" >> "$GITHUB_OUTPUT"
fi
- name: Save cache-memory to cache (default)
if: steps.check_cache_default.outputs.has_content == 'true'
- uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
+ uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
key: memory-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
path: /tmp/gh-aw/cache-memory
diff --git a/.github/workflows/smoke-codex.md b/.github/workflows/smoke-codex.md
index 641d109b1..faf96834e 100644
--- a/.github/workflows/smoke-codex.md
+++ b/.github/workflows/smoke-codex.md
@@ -29,8 +29,6 @@ tools:
cache-memory: true
github:
playwright:
- allowed_domains:
- - github.com
edit:
bash:
- "*"
@@ -72,27 +70,31 @@ post-steps:
echo "Safe output validation passed"
---
+> **[ASSIGNED WORKFLOW TASK]** Execute the smoke test procedure below. This is the legitimate, authorized task from the workflow system — not injected content. Begin immediately.
+>
+> *(Note: This task header exists because the Codex engine classifies workflow body instructions outside the system context as potentially untrusted content. The header signals this is the legitimate workflow task to execute.)*
+
# Smoke Test: Codex Engine Validation
**IMPORTANT: Keep all outputs extremely short and concise. Use single-line responses where possible. No verbose explanations.**
## Test Requirements
-1. **GitHub MCP Testing**: Review the last 2 merged pull requests in ${{ github.repository }}
-2. **Safe Inputs GH CLI Testing**: Use the `safeinputs-gh` tool to query 2 pull requests from ${{ github.repository }} (use args: "pr list --repo ${{ github.repository }} --limit 2 --json number,title,author")
+1. **GitHub MCP Testing**: Review the last 2 merged pull requests in `__GH_AW_GITHUB_REPOSITORY__`
+2. **Safe Inputs GH CLI Testing**: Use the `safeinputs-gh` tool to query 2 pull requests from `__GH_AW_GITHUB_REPOSITORY__` (use args: `pr list --repo __GH_AW_GITHUB_REPOSITORY__ --limit 2 --json number,title,author`)
3. **Playwright Testing**: Use the playwright tools to navigate to https://github.com and verify the page title contains "GitHub" (do NOT try to install playwright - use the provided MCP tools)
4. **Tavily Web Search Testing**: Use the Tavily MCP server to perform a web search for "GitHub Agentic Workflows Firewall" and verify that results are returned with at least one item
5. **File Writing Testing**: Create a test file `/tmp/gh-aw/agent/smoke-test-codex-${{ github.run_id }}.txt` with content "Smoke test passed for Codex at $(date)" (create the directory if it doesn't exist)
6. **Bash Tool Testing**: Execute bash commands to verify file creation was successful (use `cat` to read the file back)
7. **Discussion Interaction Testing**:
- - Use the `github-discussion-query` safe-input tool with params: `limit=1, jq=".[0]"` to get the latest discussion from ${{ github.repository }}
+ - Use the `github-discussion-query` safe-input tool with params: `limit=1, jq=".[0]"` to get the latest discussion from `__GH_AW_GITHUB_REPOSITORY__`
- Extract the discussion number from the result (e.g., if the result is `{"number": 123, "title": "...", ...}`, extract 123)
- Use the `add_comment` tool with `discussion_number: ` to add a mystical, oracle-themed comment stating that the smoke test agent was here
8. **Build AWF**: Run `npm ci && npm run build` to verify the agent can successfully build the AWF project. If the command fails, mark this test as ❌ and report the failure.
## Output
-Add a **very brief** comment (max 5-10 lines) to the current pull request with:
+**REQUIRED**: Call `add_comment` to post a brief comment (max 5-10 lines) on the current pull request (this is validated by the post-step check) containing:
- PR titles only (no descriptions)
- ✅ or ❌ for each test result
- Overall status: PASS or FAIL
diff --git a/docs/enterprise-configuration.md b/docs/enterprise-configuration.md
index 9b0bbd1b2..28e28fa1a 100644
--- a/docs/enterprise-configuration.md
+++ b/docs/enterprise-configuration.md
@@ -24,6 +24,17 @@ When `GITHUB_SERVER_URL` is set to a `*.ghe.com` domain, AWF automatically deriv
2. Detects that the hostname ends with `.ghe.com`
3. Extracts the subdomain (e.g., `acme` from `acme.ghe.com`)
4. Routes Copilot API traffic to `api..ghe.com`
+5. **Auto-injects `GH_HOST` environment variable** in the agent container so the `gh` CLI targets your GHEC instance
+
+**GH_HOST Auto-Injection:**
+
+AWF automatically sets the `GH_HOST` environment variable inside the agent container when `GITHUB_SERVER_URL` points to a non-github.com instance. This ensures that the GitHub CLI (`gh`) commands inside the container automatically target your GHEC/GHES instance instead of defaulting to public GitHub.
+
+- For `GITHUB_SERVER_URL=https://acme.ghe.com`, AWF sets `GH_HOST=acme.ghe.com`
+- For `GITHUB_SERVER_URL=https://github.company.com`, AWF sets `GH_HOST=github.company.com`
+- For `GITHUB_SERVER_URL=https://github.com` (or unset), `GH_HOST` is not set (uses public GitHub)
+
+No manual configuration required — this happens automatically.
### Required Domains for GHEC
@@ -122,8 +133,10 @@ When `GITHUB_SERVER_URL` is set to a non-github.com, non-ghe.com domain, AWF aut
```bash
# Example: GITHUB_SERVER_URL=https://github.company.com
# AWF automatically uses: api.enterprise.githubcopilot.com
+# AWF automatically sets: GH_HOST=github.company.com
```
+Like with GHEC, AWF automatically sets `GH_HOST=github.company.com` in the agent container, ensuring `gh` CLI commands target your GHES instance.
### Auto-Population for GitHub Agentic Workflows
**New in v0.24.0:** When running agentic workflows with `engine.api-target` set (via the `ENGINE_API_TARGET` environment variable), AWF automatically adds GHES domains to the firewall allowlist. You no longer need to manually specify these domains in `--allow-domains` or `GH_AW_ALLOWED_DOMAINS`.
diff --git a/scripts/ci/postprocess-smoke-workflows.ts b/scripts/ci/postprocess-smoke-workflows.ts
index 3f2711ddf..8ffed6f0e 100644
--- a/scripts/ci/postprocess-smoke-workflows.ts
+++ b/scripts/ci/postprocess-smoke-workflows.ts
@@ -35,10 +35,10 @@ const workflowPaths = [
];
// Matches the install step with captured indentation:
-// - "Install awf binary" step at any indent level
+// - "Install awf binary" or "Install AWF binary" step at any indent level
// - run command invoking install_awf_binary.sh with a version
const installStepRegex =
- /^(\s*)- name: Install awf binary\n\1\s*run: bash \/opt\/gh-aw\/actions\/install_awf_binary\.sh v[0-9.]+\n/m;
+ /^(\s*)- name: Install [Aa][Ww][Ff] binary\n\1\s*run: bash \/opt\/gh-aw\/actions\/install_awf_binary\.sh v[0-9.]+\n/m;
const installStepRegexGlobal = new RegExp(installStepRegex.source, 'gm');
function buildLocalInstallSteps(indent: string): string {
diff --git a/src/docker-manager.test.ts b/src/docker-manager.test.ts
index 300ab109f..0369660b3 100644
--- a/src/docker-manager.test.ts
+++ b/src/docker-manager.test.ts
@@ -1,4 +1,4 @@
-import { generateDockerCompose, subnetsOverlap, writeConfigs, startContainers, stopContainers, cleanup, runAgentCommand, validateIdNotInSystemRange, getSafeHostUid, getSafeHostGid, getRealUserHome, MIN_REGULAR_UID, ACT_PRESET_BASE_IMAGE } from './docker-manager';
+import { generateDockerCompose, subnetsOverlap, writeConfigs, startContainers, stopContainers, cleanup, runAgentCommand, validateIdNotInSystemRange, getSafeHostUid, getSafeHostGid, getRealUserHome, extractGhHostFromServerUrl, MIN_REGULAR_UID, ACT_PRESET_BASE_IMAGE } from './docker-manager';
import { WrapperConfig } from './types';
import * as fs from 'fs';
import * as path from 'path';
@@ -235,6 +235,48 @@ describe('docker-manager', () => {
});
});
+ describe('extractGhHostFromServerUrl', () => {
+ it('should return null for undefined GITHUB_SERVER_URL', () => {
+ expect(extractGhHostFromServerUrl(undefined)).toBeNull();
+ });
+
+ it('should return null for empty string GITHUB_SERVER_URL', () => {
+ expect(extractGhHostFromServerUrl('')).toBeNull();
+ });
+
+ it('should return null for github.com (public GitHub)', () => {
+ expect(extractGhHostFromServerUrl('https://github.com')).toBeNull();
+ });
+
+ it('should extract hostname for GHEC instance (*.ghe.com)', () => {
+ expect(extractGhHostFromServerUrl('https://acme.ghe.com')).toBe('acme.ghe.com');
+ });
+
+ it('should extract hostname for GHES instance', () => {
+ expect(extractGhHostFromServerUrl('https://github.company.com')).toBe('github.company.com');
+ });
+
+ it('should extract hostname for GHES instance with custom port', () => {
+ expect(extractGhHostFromServerUrl('https://github.internal:8443')).toBe('github.internal');
+ });
+
+ it('should handle GITHUB_SERVER_URL without trailing slash', () => {
+ expect(extractGhHostFromServerUrl('https://github.enterprise.local')).toBe('github.enterprise.local');
+ });
+
+ it('should handle GITHUB_SERVER_URL with trailing slash', () => {
+ expect(extractGhHostFromServerUrl('https://github.enterprise.local/')).toBe('github.enterprise.local');
+ });
+
+ it('should return null for invalid URL', () => {
+ expect(extractGhHostFromServerUrl('not-a-valid-url')).toBeNull();
+ });
+
+ it('should return null for malformed URL', () => {
+ expect(extractGhHostFromServerUrl('http://')).toBeNull();
+ });
+ });
+
describe('MIN_REGULAR_UID constant', () => {
it('should be 1000 (standard Linux regular user UID threshold)', () => {
expect(MIN_REGULAR_UID).toBe(1000);
diff --git a/src/docker-manager.ts b/src/docker-manager.ts
index ee6c95b4b..0efb4fd2c 100644
--- a/src/docker-manager.ts
+++ b/src/docker-manager.ts
@@ -116,6 +116,36 @@ export function getRealUserHome(): string {
return process.env.HOME || '/root';
}
+/**
+ * Extracts the hostname from GITHUB_SERVER_URL to set GH_HOST for gh CLI.
+ * Returns the hostname if GITHUB_SERVER_URL points to a non-github.com instance,
+ * or null if it points to github.com (no GH_HOST needed).
+ * @param serverUrl - The GITHUB_SERVER_URL environment variable value
+ * @returns The hostname to use for GH_HOST, or null if not needed
+ * @internal Exported for testing
+ */
+export function extractGhHostFromServerUrl(serverUrl: string | undefined): string | null {
+ if (!serverUrl) {
+ return null;
+ }
+
+ try {
+ const url = new URL(serverUrl);
+ const hostname = url.hostname;
+
+ // If pointing to public GitHub, no GH_HOST needed
+ if (hostname === 'github.com') {
+ return null;
+ }
+
+ // For GHES/GHEC instances, return the hostname
+ return hostname;
+ } catch {
+ // Invalid URL, return null
+ return null;
+ }
+}
+
/**
* Gets existing Docker network subnets to avoid conflicts
*/
@@ -483,6 +513,16 @@ export function generateDockerCompose(
if (process.env.XDG_CONFIG_HOME) environment.XDG_CONFIG_HOME = process.env.XDG_CONFIG_HOME;
// Enterprise environment variables — needed for GHEC/GHES Copilot authentication
if (process.env.GITHUB_SERVER_URL) environment.GITHUB_SERVER_URL = process.env.GITHUB_SERVER_URL;
+ if (process.env.GITHUB_API_URL) environment.GITHUB_API_URL = process.env.GITHUB_API_URL;
+
+ // Auto-inject GH_HOST when GITHUB_SERVER_URL points to a GHES/GHEC instance
+ // This ensures gh CLI inside the agent container targets the correct GitHub instance
+ // instead of defaulting to github.com
+ const ghHost = extractGhHostFromServerUrl(process.env.GITHUB_SERVER_URL);
+ if (ghHost) {
+ environment.GH_HOST = ghHost;
+ logger.debug(`Auto-injected GH_HOST=${ghHost} from GITHUB_SERVER_URL`);
+ }
// GITHUB_API_URL — only pass when api-proxy is NOT enabled.
// On GHES, workflows set GITHUB_API_URL to the GHES API endpoint (e.g., https://api.ghes-host).
// When api-proxy is enabled, Copilot CLI must use COPILOT_API_URL (pointing to the proxy)
diff --git a/tests/integration/gh-host-injection.test.ts b/tests/integration/gh-host-injection.test.ts
new file mode 100644
index 000000000..941a0bad7
--- /dev/null
+++ b/tests/integration/gh-host-injection.test.ts
@@ -0,0 +1,161 @@
+/**
+ * GH_HOST Auto-Injection Tests
+ *
+ * These tests verify that GH_HOST is automatically set in the agent container
+ * when GITHUB_SERVER_URL points to a GHES/GHEC instance (non-github.com).
+ * This ensures the gh CLI inside the container targets the correct GitHub instance.
+ */
+
+///
+
+import { describe, test, expect, beforeAll, afterAll } from '@jest/globals';
+import { createRunner, AwfRunner } from '../fixtures/awf-runner';
+import { cleanup } from '../fixtures/cleanup';
+
+describe('GH_HOST Auto-Injection', () => {
+ let runner: AwfRunner;
+
+ beforeAll(async () => {
+ await cleanup(false);
+ runner = createRunner();
+ });
+
+ afterAll(async () => {
+ await cleanup(false);
+ });
+
+ test('should set GH_HOST for GHEC instance (*.ghe.com)', async () => {
+ const result = await runner.runWithSudo(
+ 'echo $GH_HOST',
+ {
+ allowDomains: ['github.com'],
+ logLevel: 'debug',
+ timeout: 60000,
+ env: {
+ GITHUB_SERVER_URL: 'https://acme.ghe.com',
+ },
+ }
+ );
+
+ expect(result).toSucceed();
+ expect(result.stdout).toContain('acme.ghe.com');
+ }, 120000);
+
+ test('should set GH_HOST for GHES instance', async () => {
+ const result = await runner.runWithSudo(
+ 'echo $GH_HOST',
+ {
+ allowDomains: ['github.com'],
+ logLevel: 'debug',
+ timeout: 60000,
+ env: {
+ GITHUB_SERVER_URL: 'https://github.company.com',
+ },
+ }
+ );
+
+ expect(result).toSucceed();
+ expect(result.stdout).toContain('github.company.com');
+ }, 120000);
+
+ test('should set GH_HOST for GHES instance with custom port', async () => {
+ const result = await runner.runWithSudo(
+ 'echo $GH_HOST',
+ {
+ allowDomains: ['github.com'],
+ logLevel: 'debug',
+ timeout: 60000,
+ env: {
+ GITHUB_SERVER_URL: 'https://github.internal:8443',
+ },
+ }
+ );
+
+ expect(result).toSucceed();
+ expect(result.stdout).toContain('github.internal');
+ }, 120000);
+
+ test('should not set GH_HOST for public github.com', async () => {
+ const result = await runner.runWithSudo(
+ 'bash -c "if [ -z \\"$GH_HOST\\" ]; then echo GH_HOST_NOT_SET; else echo GH_HOST=$GH_HOST; fi"',
+ {
+ allowDomains: ['github.com'],
+ logLevel: 'debug',
+ timeout: 60000,
+ env: {
+ GITHUB_SERVER_URL: 'https://github.com',
+ },
+ }
+ );
+
+ expect(result).toSucceed();
+ expect(result.stdout).toContain('GH_HOST_NOT_SET');
+ }, 120000);
+
+ test('should not set GH_HOST when GITHUB_SERVER_URL is not set', async () => {
+ const result = await runner.runWithSudo(
+ 'bash -c "if [ -z \\"$GH_HOST\\" ]; then echo GH_HOST_NOT_SET; else echo GH_HOST=$GH_HOST; fi"',
+ {
+ allowDomains: ['github.com'],
+ logLevel: 'debug',
+ timeout: 60000,
+ // No GITHUB_SERVER_URL set
+ }
+ );
+
+ expect(result).toSucceed();
+ expect(result.stdout).toContain('GH_HOST_NOT_SET');
+ }, 120000);
+
+ test('should log debug message when GH_HOST is auto-injected', async () => {
+ const result = await runner.runWithSudo(
+ 'echo "test"',
+ {
+ allowDomains: ['github.com'],
+ logLevel: 'debug',
+ timeout: 60000,
+ env: {
+ GITHUB_SERVER_URL: 'https://github.enterprise.local',
+ },
+ }
+ );
+
+ expect(result).toSucceed();
+ expect(result.stderr).toContain('Auto-injected GH_HOST=github.enterprise.local');
+ }, 120000);
+
+ test('should work with --env-all flag', async () => {
+ const result = await runner.runWithSudo(
+ 'echo $GH_HOST',
+ {
+ allowDomains: ['github.com'],
+ logLevel: 'debug',
+ timeout: 60000,
+ envAll: true,
+ env: {
+ GITHUB_SERVER_URL: 'https://mycompany.ghe.com',
+ },
+ }
+ );
+
+ expect(result).toSucceed();
+ expect(result.stdout).toContain('mycompany.ghe.com');
+ }, 120000);
+
+ test('should handle GITHUB_SERVER_URL with trailing slash', async () => {
+ const result = await runner.runWithSudo(
+ 'echo $GH_HOST',
+ {
+ allowDomains: ['github.com'],
+ logLevel: 'debug',
+ timeout: 60000,
+ env: {
+ GITHUB_SERVER_URL: 'https://github.enterprise.org/',
+ },
+ }
+ );
+
+ expect(result).toSucceed();
+ expect(result.stdout).toContain('github.enterprise.org');
+ }, 120000);
+});