diff --git a/.changeset/patch-update-changeset-generator-to-codex-gpt-5-mini.md b/.changeset/patch-update-changeset-generator-to-codex-gpt-5-mini.md new file mode 100644 index 00000000000..a6729166046 --- /dev/null +++ b/.changeset/patch-update-changeset-generator-to-codex-gpt-5-mini.md @@ -0,0 +1,10 @@ +--- +"gh-aw": patch +--- + +Update the changeset generator workflow to use the `codex` engine with the +`gpt-5-mini` model. Add `strict: false` and remove the `firewall: true` network +setting to accommodate the codex engine's network behavior. + +This is an internal tooling change. + diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml index 1e114a7b582..a434bec2756 100644 --- a/.github/workflows/changeset.lock.yml +++ b/.github/workflows/changeset.lock.yml @@ -35,7 +35,10 @@ # contents: read # pull-requests: read # issues: read -# engine: copilot +# engine: +# id: codex +# model: gpt-5-mini +# strict: false # Required: codex engine doesn't support network firewall # safe-outputs: # push-to-pull-request-branch: # commit-title-suffix: " [skip-ci]" @@ -48,7 +51,6 @@ # allowed: # - defaults # - node -# firewall: true # tools: # bash: # - "*" @@ -1284,37 +1286,29 @@ jobs: main().catch(error => { core.setFailed(error instanceof Error ? error.message : String(error)); }); - - name: Validate COPILOT_GITHUB_TOKEN or COPILOT_CLI_TOKEN secret + - name: Validate CODEX_API_KEY or OPENAI_API_KEY secret run: | - if [ -z "$COPILOT_GITHUB_TOKEN" ] && [ -z "$COPILOT_CLI_TOKEN" ]; then - echo "Error: Neither COPILOT_GITHUB_TOKEN nor COPILOT_CLI_TOKEN secret is set" - echo "The GitHub Copilot CLI engine requires either COPILOT_GITHUB_TOKEN or COPILOT_CLI_TOKEN secret to be configured." + if [ -z "$CODEX_API_KEY" ] && [ -z "$OPENAI_API_KEY" ]; then + echo "Error: Neither CODEX_API_KEY nor OPENAI_API_KEY secret is set" + echo "The Codex engine requires either CODEX_API_KEY or OPENAI_API_KEY secret to be configured." echo "Please configure one of these secrets in your repository settings." - echo "Documentation: https://githubnext.github.io/gh-aw/reference/engines/#github-copilot-default" + echo "Documentation: https://githubnext.github.io/gh-aw/reference/engines/#openai-codex" exit 1 fi - if [ -n "$COPILOT_GITHUB_TOKEN" ]; then - echo "COPILOT_GITHUB_TOKEN secret is configured" + if [ -n "$CODEX_API_KEY" ]; then + echo "CODEX_API_KEY secret is configured" else - echo "COPILOT_CLI_TOKEN secret is configured (using as fallback for COPILOT_GITHUB_TOKEN)" + echo "OPENAI_API_KEY secret is configured (using as fallback for CODEX_API_KEY)" fi env: - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - COPILOT_CLI_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} + CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - name: Setup Node.js uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 with: node-version: '24' - - name: Install awf binary - run: | - echo "Installing awf from release: v0.5.0" - curl -L https://github.com/githubnext/gh-aw-firewall/releases/download/v0.5.0/awf-linux-x64 -o awf - chmod +x awf - sudo mv awf /usr/local/bin/ - which awf - awf --version - - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.365 + - name: Install Codex + run: npm install -g @openai/codex@0.63.0 - name: Downloading container images run: | set -e @@ -2178,54 +2172,40 @@ jobs: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} run: | mkdir -p /tmp/gh-aw/mcp-config - mkdir -p /home/runner/.copilot - cat > /home/runner/.copilot/mcp-config.json << EOF - { - "mcpServers": { - "github": { - "type": "local", - "command": "docker", - "args": [ - "run", - "-i", - "--rm", - "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN", - "-e", - "GITHUB_READ_ONLY=1", - "-e", - "GITHUB_TOOLSETS=default", - "ghcr.io/github/github-mcp-server:v0.23.0" - ], - "tools": ["*"], - "env": { - "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}" - } - }, - "safeoutputs": { - "type": "local", - "command": "node", - "args": ["/tmp/gh-aw/safeoutputs/mcp-server.cjs"], - "tools": ["*"], - "env": { - "GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}", - "GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}", - "GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}", - "GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}", - "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}", - "GITHUB_SERVER_URL": "\${GITHUB_SERVER_URL}" - } - } - } - } + cat > /tmp/gh-aw/mcp-config/config.toml << EOF + [history] + persistence = "none" + + [shell_environment_policy] + inherit = "core" + include_only = ["CODEX_API_KEY", "GH_AW_ASSETS_ALLOWED_EXTS", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_SAFE_OUTPUTS", "GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "HOME", "OPENAI_API_KEY", "PATH"] + + [mcp_servers.github] + user_agent = "changeset-generator" + startup_timeout_sec = 120 + tool_timeout_sec = 60 + command = "docker" + args = [ + "run", + "-i", + "--rm", + "-e", + "GITHUB_PERSONAL_ACCESS_TOKEN", + "-e", + "GITHUB_READ_ONLY=1", + "-e", + "GITHUB_TOOLSETS=default", + "ghcr.io/github/github-mcp-server:v0.23.0" + ] + env_vars = ["GITHUB_PERSONAL_ACCESS_TOKEN"] + + [mcp_servers.safeoutputs] + command = "node" + args = [ + "/tmp/gh-aw/safeoutputs/mcp-server.cjs", + ] + env_vars = ["GH_AW_SAFE_OUTPUTS", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_ASSETS_ALLOWED_EXTS", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL"] EOF - echo "-------START MCP CONFIG-----------" - cat /home/runner/.copilot/mcp-config.json - echo "-------END MCP CONFIG-----------" - echo "-------/home/runner/.copilot-----------" - find /home/runner/.copilot - echo "HOME: $HOME" - echo "GITHUB_COPILOT_CLI_MODE: $GITHUB_COPILOT_CLI_MODE" - name: Generate agentic run info uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: @@ -2233,13 +2213,13 @@ jobs: const fs = require('fs'); const awInfo = { - engine_id: "copilot", - engine_name: "GitHub Copilot CLI", - model: "", + engine_id: "codex", + engine_name: "Codex", + model: "gpt-5-mini", version: "", - agent_version: "0.0.365", + agent_version: "0.63.0", workflow_name: "Changeset Generator", - experimental: false, + experimental: true, supports_tools_allowlist: true, supports_http_transport: true, run_id: context.runId, @@ -2253,10 +2233,10 @@ jobs: staged: false, network_mode: "defaults", allowed_domains: ["defaults","node"], - firewall_enabled: true, + firewall_enabled: false, firewall_version: "", steps: { - firewall: "squid" + firewall: "" }, created_at: new Date().toISOString() }; @@ -2733,37 +2713,23 @@ jobs: name: aw_info.json path: /tmp/gh-aw/aw_info.json if-no-files-found: warn - - name: Execute GitHub Copilot CLI - id: agentic_execution - # Copilot CLI tool arguments (sorted): - timeout-minutes: 20 + - name: Run Codex run: | set -o pipefail - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount "${GITHUB_WORKSPACE}/.github:/workspace/.github:rw" --allow-domains api.enterprise.githubcopilot.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,crl.geotrust.com,crl.globalsign.com,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,get.pnpm.io,github.com,json-schema.org,json.schemastore.org,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,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com --log-level info \ - -- npx -y @github/copilot@0.0.365 --add-dir /tmp/gh-aw/ --log-level all --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 /tmp/gh-aw/agent-stdio.log - - # Move preserved agent logs to expected location - # Try new naming convention first (awf-agent-logs-*), fall back to legacy (copilot-logs-*) for backward compatibility - AGENT_LOGS_DIR="$(find /tmp -maxdepth 1 -type d \( -name 'awf-agent-logs-*' -o -name 'copilot-logs-*' \) -print0 2>/dev/null | xargs -0 ls -td 2>/dev/null | head -1)" - if [ -n "$AGENT_LOGS_DIR" ] && [ -d "$AGENT_LOGS_DIR" ]; then - echo "Moving agent logs from $AGENT_LOGS_DIR to /tmp/gh-aw/.agent/logs/" - sudo mkdir -p /tmp/gh-aw/.agent/logs/ - sudo mv "$AGENT_LOGS_DIR"/* /tmp/gh-aw/.agent/logs/ || true - sudo rmdir "$AGENT_LOGS_DIR" || true - fi + INSTRUCTION="$(cat "$GH_AW_PROMPT")" + mkdir -p "$CODEX_HOME/logs" + codex -c model=gpt-5-mini exec --full-auto --skip-git-repo-check "$INSTRUCTION" 2>&1 | tee /tmp/gh-aw/agent-stdio.log env: - COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN || secrets.COPILOT_CLI_TOKEN }} - GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} + CODEX_HOME: /tmp/gh-aw/mcp-config + GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GH_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/config.toml GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} - GITHUB_WORKSPACE: ${{ github.workspace }} - XDG_CONFIG_HOME: /home/runner + 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: Redact secrets in logs if: always() uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -2875,11 +2841,11 @@ jobs: } await main(); env: - GH_AW_SECRET_NAMES: 'COPILOT_CLI_TOKEN,COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' - SECRET_COPILOT_CLI_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} - SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + GH_AW_SECRET_NAMES: 'CODEX_API_KEY,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY' + SECRET_CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }} SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - name: Upload Safe Outputs if: always() uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 @@ -2892,7 +2858,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "api.enterprise.githubcopilot.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,crl.geotrust.com,crl.globalsign.com,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,get.pnpm.io,github.com,json-schema.org,json.schemastore.org,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,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" + GH_AW_ALLOWED_DOMAINS: "crl3.digicert.com,crl4.digicert.com,ocsp.digicert.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,crl.geotrust.com,ocsp.geotrust.com,crl.thawte.com,ocsp.thawte.com,crl.verisign.com,ocsp.verisign.com,crl.globalsign.com,ocsp.globalsign.com,crls.ssl.com,ocsp.ssl.com,crl.identrust.com,ocsp.identrust.com,crl.sectigo.com,ocsp.sectigo.com,crl.usertrust.com,ocsp.usertrust.com,s.symcb.com,s.symcd.com,json-schema.org,json.schemastore.org,archive.ubuntu.com,security.ubuntu.com,ppa.launchpad.net,keyserver.ubuntu.com,azure.archive.ubuntu.com,api.snapcraft.io,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,npmjs.org,npmjs.com,www.npmjs.com,www.npmjs.org,registry.npmjs.com,registry.npmjs.org,skimdb.npmjs.com,npm.pkg.github.com,api.npms.io,nodejs.org,yarnpkg.com,registry.yarnpkg.com,repo.yarnpkg.com,deb.nodesource.com,get.pnpm.io,bun.sh,deno.land,registry.bower.io" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} with: @@ -4094,7 +4060,7 @@ jobs: with: name: agent_outputs path: | - /tmp/gh-aw/.agent/logs/ + /tmp/gh-aw/mcp-config/logs/ /tmp/gh-aw/redacted-urls.log if-no-files-found: ignore - name: Upload MCP logs @@ -4108,7 +4074,7 @@ jobs: if: always() uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: - GH_AW_AGENT_OUTPUT: /tmp/gh-aw/.agent/logs/ + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log with: script: | function runLogParser(options) { @@ -4643,832 +4609,316 @@ jobs: } function main() { runLogParser({ - parseLog: parseCopilotLog, - parserName: "Copilot", - supportsDirectories: true, + parseLog: parseCodexLog, + parserName: "Codex", + supportsDirectories: false, }); } - function extractPremiumRequestCount(logContent) { - const patterns = [ - /premium\s+requests?\s+consumed:?\s*(\d+)/i, - /(\d+)\s+premium\s+requests?\s+consumed/i, - /consumed\s+(\d+)\s+premium\s+requests?/i, - ]; - for (const pattern of patterns) { - const match = logContent.match(pattern); - if (match && match[1]) { - const count = parseInt(match[1], 10); - if (!isNaN(count) && count > 0) { - return count; + function extractMCPInitialization(lines) { + const mcpServers = new Map(); + let serverCount = 0; + let connectedCount = 0; + let availableTools = []; + for (const line of lines) { + if (line.includes("Initializing MCP servers") || (line.includes("mcp") && line.includes("init"))) { + } + const countMatch = line.match(/Found (\d+) MCP servers? in configuration/i); + if (countMatch) { + serverCount = parseInt(countMatch[1]); + } + const connectingMatch = line.match(/Connecting to MCP server[:\s]+['"]?(\w+)['"]?/i); + if (connectingMatch) { + const serverName = connectingMatch[1]; + if (!mcpServers.has(serverName)) { + mcpServers.set(serverName, { name: serverName, status: "connecting" }); } } - } - return 1; - } - function parseCopilotLog(logContent) { - try { - let logEntries; - try { - logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { - throw new Error("Not a JSON array"); - } - } catch (jsonArrayError) { - const debugLogEntries = parseDebugLogFormat(logContent); - if (debugLogEntries && debugLogEntries.length > 0) { - logEntries = debugLogEntries; - } else { - logEntries = parseLogEntries(logContent); + const connectedMatch = line.match(/MCP server ['"](\w+)['"] connected successfully/i); + if (connectedMatch) { + const serverName = connectedMatch[1]; + mcpServers.set(serverName, { name: serverName, status: "connected" }); + connectedCount++; + } + const failedMatch = line.match(/Failed to connect to MCP server ['"](\w+)['"][:]\s*(.+)/i); + if (failedMatch) { + const serverName = failedMatch[1]; + const error = failedMatch[2].trim(); + mcpServers.set(serverName, { name: serverName, status: "failed", error }); + } + const initFailedMatch = line.match(/MCP server ['"](\w+)['"] initialization failed/i); + if (initFailedMatch) { + const serverName = initFailedMatch[1]; + const existing = mcpServers.get(serverName); + if (existing && existing.status !== "failed") { + mcpServers.set(serverName, { name: serverName, status: "failed", error: "Initialization failed" }); } } - if (!logEntries) { - return "## Agent Log Summary\n\nLog format not recognized as Copilot JSON array or JSONL.\n"; - } - const conversationResult = generateConversationMarkdown(logEntries, { - formatToolCallback: (toolUse, toolResult) => formatToolUse(toolUse, toolResult, { includeDetailedParameters: true }), - formatInitCallback: initEntry => - formatInitializationSummary(initEntry, { - includeSlashCommands: false, - modelInfoCallback: entry => { - if (!entry.model_info) return ""; - const modelInfo = entry.model_info; - let markdown = ""; - if (modelInfo.name) { - markdown += `**Model Name:** ${modelInfo.name}`; - if (modelInfo.vendor) { - markdown += ` (${modelInfo.vendor})`; - } - markdown += "\n\n"; - } - if (modelInfo.billing) { - const billing = modelInfo.billing; - if (billing.is_premium === true) { - markdown += `**Premium Model:** Yes`; - if (billing.multiplier && billing.multiplier !== 1) { - markdown += ` (${billing.multiplier}x cost multiplier)`; - } - markdown += "\n"; - if (billing.restricted_to && Array.isArray(billing.restricted_to) && billing.restricted_to.length > 0) { - markdown += `**Required Plans:** ${billing.restricted_to.join(", ")}\n`; - } - markdown += "\n"; - } else if (billing.is_premium === false) { - markdown += `**Premium Model:** No\n\n`; - } - } - return markdown; - }, - }), - }); - let markdown = conversationResult.markdown; - const lastEntry = logEntries[logEntries.length - 1]; - const initEntry = logEntries.find(entry => entry.type === "system" && entry.subtype === "init"); - markdown += generateInformationSection(lastEntry, { - additionalInfoCallback: entry => { - const isPremiumModel = - initEntry && initEntry.model_info && initEntry.model_info.billing && initEntry.model_info.billing.is_premium === true; - if (isPremiumModel) { - const premiumRequestCount = extractPremiumRequestCount(logContent); - return `**Premium Requests Consumed:** ${premiumRequestCount}\n\n`; - } - return ""; - }, - }); - return markdown; - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - return `## Agent Log Summary\n\nError parsing Copilot log (tried both JSON array and JSONL formats): ${errorMessage}\n`; + const toolsMatch = line.match(/Available tools:\s*(.+)/i); + if (toolsMatch) { + const toolsStr = toolsMatch[1]; + availableTools = toolsStr + .split(",") + .map(t => t.trim()) + .filter(t => t.length > 0); + } } - } - function scanForToolErrors(logContent) { - const toolErrors = new Map(); - const lines = logContent.split("\n"); - const recentToolCalls = []; - const MAX_RECENT_TOOLS = 10; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - if (line.includes('"tool_calls":') && !line.includes('\\"tool_calls\\"')) { - for (let j = i + 1; j < Math.min(i + 30, lines.length); j++) { - const nextLine = lines[j]; - const idMatch = nextLine.match(/"id":\s*"([^"]+)"/); - const nameMatch = nextLine.match(/"name":\s*"([^"]+)"/) && !nextLine.includes('\\"name\\"'); - if (idMatch) { - const toolId = idMatch[1]; - for (let k = j; k < Math.min(j + 10, lines.length); k++) { - const nameLine = lines[k]; - const funcNameMatch = nameLine.match(/"name":\s*"([^"]+)"/); - if (funcNameMatch && !nameLine.includes('\\"name\\"')) { - const toolName = funcNameMatch[1]; - recentToolCalls.unshift({ id: toolId, name: toolName }); - if (recentToolCalls.length > MAX_RECENT_TOOLS) { - recentToolCalls.pop(); - } - break; - } - } - } - } + let markdown = ""; + const hasInfo = mcpServers.size > 0 || availableTools.length > 0; + if (mcpServers.size > 0) { + markdown += "**MCP Servers:**\n"; + const servers = Array.from(mcpServers.values()); + const connected = servers.filter(s => s.status === "connected"); + const failed = servers.filter(s => s.status === "failed"); + markdown += `- Total: ${servers.length}${serverCount > 0 && servers.length !== serverCount ? ` (configured: ${serverCount})` : ""}\n`; + markdown += `- Connected: ${connected.length}\n`; + if (failed.length > 0) { + markdown += `- Failed: ${failed.length}\n`; } - const errorMatch = line.match(/\[ERROR\].*(?:Tool execution failed|Permission denied|Resource not accessible|Error executing tool)/i); - if (errorMatch) { - const toolNameMatch = line.match(/Tool execution failed:\s*([^\s]+)/i); - const toolIdMatch = line.match(/tool_call_id:\s*([^\s]+)/i); - if (toolNameMatch) { - const toolName = toolNameMatch[1]; - toolErrors.set(toolName, true); - const matchingTool = recentToolCalls.find(t => t.name === toolName); - if (matchingTool) { - toolErrors.set(matchingTool.id, true); - } - } else if (toolIdMatch) { - toolErrors.set(toolIdMatch[1], true); - } else if (recentToolCalls.length > 0) { - const lastTool = recentToolCalls[0]; - toolErrors.set(lastTool.id, true); - toolErrors.set(lastTool.name, true); + markdown += "\n"; + for (const server of servers) { + const statusIcon = server.status === "connected" ? "✅" : server.status === "failed" ? "❌" : "⏳"; + markdown += `- ${statusIcon} **${server.name}** (${server.status})`; + if (server.error) { + markdown += `\n - Error: ${server.error}`; } + markdown += "\n"; } + markdown += "\n"; + } + if (availableTools.length > 0) { + markdown += "**Available MCP Tools:**\n"; + markdown += `- Total: ${availableTools.length} tools\n`; + markdown += `- Tools: ${availableTools.slice(0, 10).join(", ")}${availableTools.length > 10 ? ", ..." : ""}\n\n`; } - return toolErrors; + return { + hasInfo, + markdown, + servers: Array.from(mcpServers.values()), + }; } - function parseDebugLogFormat(logContent) { - const entries = []; - const lines = logContent.split("\n"); - const toolErrors = scanForToolErrors(logContent); - let model = "unknown"; - let sessionId = null; - let modelInfo = null; - let tools = []; - const modelMatch = logContent.match(/Starting Copilot CLI: ([\d.]+)/); - if (modelMatch) { - sessionId = `copilot-${modelMatch[1]}-${Date.now()}`; - } - const gotModelInfoIndex = logContent.indexOf("[DEBUG] Got model info: {"); - if (gotModelInfoIndex !== -1) { - const jsonStart = logContent.indexOf("{", gotModelInfoIndex); - if (jsonStart !== -1) { - let braceCount = 0; - let inString = false; - let escapeNext = false; - let jsonEnd = -1; - for (let i = jsonStart; i < logContent.length; i++) { - const char = logContent[i]; - if (escapeNext) { - escapeNext = false; - continue; - } - if (char === "\\") { - escapeNext = true; - continue; - } - if (char === '"' && !escapeNext) { - inString = !inString; - continue; - } - if (inString) continue; - if (char === "{") { - braceCount++; - } else if (char === "}") { - braceCount--; - if (braceCount === 0) { - jsonEnd = i + 1; - break; - } - } + function parseCodexLog(logContent) { + try { + const lines = logContent.split("\n"); + const LOOKAHEAD_WINDOW = 50; + let markdown = ""; + const mcpInfo = extractMCPInitialization(lines); + if (mcpInfo.hasInfo) { + markdown += "## 🚀 Initialization\n\n"; + markdown += mcpInfo.markdown; + } + markdown += "## 🤖 Reasoning\n\n"; + let inThinkingSection = false; + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if ( + line.includes("OpenAI Codex") || + line.startsWith("--------") || + line.includes("workdir:") || + line.includes("model:") || + line.includes("provider:") || + line.includes("approval:") || + line.includes("sandbox:") || + line.includes("reasoning effort:") || + line.includes("reasoning summaries:") || + line.includes("tokens used:") || + line.includes("DEBUG codex") || + line.includes("INFO codex") || + line.match(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z\s+(DEBUG|INFO|WARN|ERROR)/) + ) { + continue; } - if (jsonEnd !== -1) { - const modelInfoJson = logContent.substring(jsonStart, jsonEnd); - try { - modelInfo = JSON.parse(modelInfoJson); - } catch (e) { - } + if (line.trim() === "thinking") { + inThinkingSection = true; + continue; } - } - } - const toolsIndex = logContent.indexOf("[DEBUG] Tools:"); - if (toolsIndex !== -1) { - const afterToolsLine = logContent.indexOf("\n", toolsIndex); - let toolsStart = logContent.indexOf("[DEBUG] [", afterToolsLine); - if (toolsStart !== -1) { - toolsStart = logContent.indexOf("[", toolsStart + 7); - } - if (toolsStart !== -1) { - let bracketCount = 0; - let inString = false; - let escapeNext = false; - let toolsEnd = -1; - for (let i = toolsStart; i < logContent.length; i++) { - const char = logContent[i]; - if (escapeNext) { - escapeNext = false; - continue; - } - if (char === "\\") { - escapeNext = true; - continue; - } - if (char === '"' && !escapeNext) { - inString = !inString; - continue; - } - if (inString) continue; - if (char === "[") { - bracketCount++; - } else if (char === "]") { - bracketCount--; - if (bracketCount === 0) { - toolsEnd = i + 1; + const toolMatch = line.match(/^tool\s+(\w+)\.(\w+)\(/); + if (toolMatch) { + inThinkingSection = false; + const server = toolMatch[1]; + const toolName = toolMatch[2]; + let statusIcon = "❓"; + for (let j = i + 1; j < Math.min(i + LOOKAHEAD_WINDOW, lines.length); j++) { + const nextLine = lines[j]; + if (nextLine.includes(`${server}.${toolName}(`) && nextLine.includes("success in")) { + statusIcon = "✅"; + break; + } else if (nextLine.includes(`${server}.${toolName}(`) && (nextLine.includes("failed in") || nextLine.includes("error"))) { + statusIcon = "❌"; break; } } + markdown += `${statusIcon} ${server}::${toolName}(...)\n\n`; + continue; } - if (toolsEnd !== -1) { - let toolsJson = logContent.substring(toolsStart, toolsEnd); - toolsJson = toolsJson.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /gm, ""); - try { - const toolsArray = JSON.parse(toolsJson); - if (Array.isArray(toolsArray)) { - tools = toolsArray - .map(tool => { - if (tool.type === "function" && tool.function && tool.function.name) { - let name = tool.function.name; - if (name.startsWith("github-")) { - name = "mcp__github__" + name.substring(7); - } else if (name.startsWith("safe_outputs-")) { - name = name; - } - return name; - } - return null; - }) - .filter(name => name !== null); - } - } catch (e) { - } + if (inThinkingSection && line.trim().length > 20 && !line.match(/^\d{4}-\d{2}-\d{2}T/)) { + const trimmed = line.trim(); + markdown += `${trimmed}\n\n`; } } - } - let inDataBlock = false; - let currentJsonLines = []; - let turnCount = 0; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - if (line.includes("[DEBUG] data:")) { - inDataBlock = true; - currentJsonLines = []; - continue; - } - if (inDataBlock) { - const hasTimestamp = line.match(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z /); - if (hasTimestamp) { - const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); - const isJsonContent = /^[{\[}\]"]/.test(cleanLine) || cleanLine.trim().startsWith('"'); - if (!isJsonContent) { - if (currentJsonLines.length > 0) { - try { - const jsonStr = currentJsonLines.join("\n"); - const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; + markdown += "## 🤖 Commands and Tools\n\n"; + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const toolMatch = line.match(/^\[.*?\]\s+tool\s+(\w+)\.(\w+)\((.+)\)/) || line.match(/ToolCall:\s+(\w+)__(\w+)\s+(\{.+\})/); + const bashMatch = line.match(/^\[.*?\]\s+exec\s+bash\s+-lc\s+'([^']+)'/); + if (toolMatch) { + const server = toolMatch[1]; + const toolName = toolMatch[2]; + const params = toolMatch[3]; + let statusIcon = "❓"; + let response = ""; + let isError = false; + for (let j = i + 1; j < Math.min(i + LOOKAHEAD_WINDOW, lines.length); j++) { + const nextLine = lines[j]; + if (nextLine.includes(`${server}.${toolName}(`) && (nextLine.includes("success in") || nextLine.includes("failed in"))) { + isError = nextLine.includes("failed in"); + statusIcon = isError ? "❌" : "✅"; + let jsonLines = []; + let braceCount = 0; + let inJson = false; + for (let k = j + 1; k < Math.min(j + 30, lines.length); k++) { + const respLine = lines[k]; + if (respLine.includes("tool ") || respLine.includes("ToolCall:") || respLine.includes("tokens used")) { + break; } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - const originalToolName = toolName; - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; - } - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - const hasError = toolErrors.has(toolId) || toolErrors.has(originalToolName); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: hasError ? "Permission denied or tool execution failed" : "", - is_error: hasError, - }); - } - } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { - entries.push({ - type: "user", - message: { content: toolResults }, - }); - } - } - } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, - }; - } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; + for (const char of respLine) { + if (char === "{") { + braceCount++; + inJson = true; + } else if (char === "}") { + braceCount--; } } - } catch (e) { - } - } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } - } else { - const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); - currentJsonLines.push(cleanLine); - } - } - } - if (inDataBlock && currentJsonLines.length > 0) { - try { - const jsonStr = currentJsonLines.join("\n"); - const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - const originalToolName = toolName; - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; - } - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - const hasError = toolErrors.has(toolId) || toolErrors.has(originalToolName); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: hasError ? "Permission denied or tool execution failed" : "", - is_error: hasError, - }); - } + if (inJson) { + jsonLines.push(respLine); } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { - entries.push({ - type: "user", - message: { content: toolResults }, - }); + if (inJson && braceCount === 0) { + break; } } + response = jsonLines.join("\n"); + break; } } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, - }; - } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + markdown += formatCodexToolCall(server, toolName, params, response, statusIcon); + } else if (bashMatch) { + const command = bashMatch[1]; + let statusIcon = "❓"; + let response = ""; + let isError = false; + for (let j = i + 1; j < Math.min(i + LOOKAHEAD_WINDOW, lines.length); j++) { + const nextLine = lines[j]; + if (nextLine.includes("bash -lc") && (nextLine.includes("succeeded in") || nextLine.includes("failed in"))) { + isError = nextLine.includes("failed in"); + statusIcon = isError ? "❌" : "✅"; + let responseLines = []; + for (let k = j + 1; k < Math.min(j + 20, lines.length); k++) { + const respLine = lines[k]; + if ( + respLine.includes("tool ") || + respLine.includes("exec ") || + respLine.includes("ToolCall:") || + respLine.includes("tokens used") || + respLine.includes("thinking") + ) { + break; + } + responseLines.push(respLine); + } + response = responseLines.join("\n").trim(); + break; } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + markdown += formatCodexBashCall(command, response, statusIcon); } - } catch (e) { } - } - if (entries.length > 0) { - const initEntry = { - type: "system", - subtype: "init", - session_id: sessionId, - model: model, - tools: tools, - }; - if (modelInfo) { - initEntry.model_info = modelInfo; + markdown += "\n## 📊 Information\n\n"; + let totalTokens = 0; + const tokenCountMatches = logContent.matchAll(/total_tokens:\s*(\d+)/g); + for (const match of tokenCountMatches) { + const tokens = parseInt(match[1]); + totalTokens = Math.max(totalTokens, tokens); } - entries.unshift(initEntry); - if (entries._lastResult) { - entries.push(entries._lastResult); - delete entries._lastResult; + const finalTokensMatch = logContent.match(/tokens used\n([\d,]+)/); + if (finalTokensMatch) { + totalTokens = parseInt(finalTokensMatch[1].replace(/,/g, "")); } - } - return entries; - } - if (typeof module !== "undefined" && module.exports) { - module.exports = { - parseCopilotLog, - extractPremiumRequestCount, - }; - } - main(); - - name: Agent Firewall logs - if: always() - run: | - # Squid logs are preserved in timestamped directories - SQUID_LOGS_DIR="$(find /tmp -maxdepth 1 -type d -name 'squid-logs-*' -print0 2>/dev/null | xargs -0 ls -td 2>/dev/null | head -1)" - if [ -n "$SQUID_LOGS_DIR" ] && [ -d "$SQUID_LOGS_DIR" ]; then - echo "Found Squid logs at: $SQUID_LOGS_DIR" - mkdir -p /tmp/gh-aw/squid-logs-changeset-generator/ - sudo cp -r "$SQUID_LOGS_DIR"/* /tmp/gh-aw/squid-logs-changeset-generator/ || true - sudo chmod -R a+r /tmp/gh-aw/squid-logs-changeset-generator/ || true - fi - - name: Upload Firewall Logs - if: always() - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 - with: - name: squid-logs-changeset-generator - path: /tmp/gh-aw/squid-logs-changeset-generator/ - if-no-files-found: ignore - - name: Parse firewall logs for step summary - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - - function main() { - - const fs = require("fs"); - - const path = require("path"); - - try { - - const workflowName = process.env.GITHUB_WORKFLOW || "workflow"; - - const sanitizedName = sanitizeWorkflowName(workflowName); - - const squidLogsDir = `/tmp/gh-aw/squid-logs-${sanitizedName}/`; - - if (!fs.existsSync(squidLogsDir)) { - - core.info(`No firewall logs directory found at: ${squidLogsDir}`); - - return; - + if (totalTokens > 0) { + markdown += `**Total Tokens Used:** ${totalTokens.toLocaleString()}\n\n`; } - - const files = fs.readdirSync(squidLogsDir).filter(file => file.endsWith(".log")); - - if (files.length === 0) { - - core.info(`No firewall log files found in: ${squidLogsDir}`); - - return; - - } - - core.info(`Found ${files.length} firewall log file(s)`); - - let totalRequests = 0; - - let allowedRequests = 0; - - let deniedRequests = 0; - - const allowedDomains = new Set(); - - const deniedDomains = new Set(); - - const requestsByDomain = new Map(); - - for (const file of files) { - - const filePath = path.join(squidLogsDir, file); - - core.info(`Parsing firewall log: ${file}`); - - const content = fs.readFileSync(filePath, "utf8"); - - const lines = content.split("\n").filter(line => line.trim()); - - for (const line of lines) { - - const entry = parseFirewallLogLine(line); - - if (!entry) { - - continue; - - } - - totalRequests++; - - const isAllowed = isRequestAllowed(entry.decision, entry.status); - - if (isAllowed) { - - allowedRequests++; - - allowedDomains.add(entry.domain); - - } else { - - deniedRequests++; - - deniedDomains.add(entry.domain); - - } - - if (!requestsByDomain.has(entry.domain)) { - - requestsByDomain.set(entry.domain, { allowed: 0, denied: 0 }); - - } - - const domainStats = requestsByDomain.get(entry.domain); - - if (isAllowed) { - - domainStats.allowed++; - - } else { - - domainStats.denied++; - - } - - } - + const toolCalls = (logContent.match(/ToolCall:\s+\w+__\w+/g) || []).length; + if (toolCalls > 0) { + markdown += `**Tool Calls:** ${toolCalls}\n\n`; } - - const summary = generateFirewallSummary({ - - totalRequests, - - allowedRequests, - - deniedRequests, - - allowedDomains: Array.from(allowedDomains).sort(), - - deniedDomains: Array.from(deniedDomains).sort(), - - requestsByDomain, - - }); - - core.summary.addRaw(summary).write(); - - core.info("Firewall log summary generated successfully"); - + return markdown; } catch (error) { - - core.setFailed(error instanceof Error ? error : String(error)); - + core.error(`Error parsing Codex log: ${error}`); + return "## 🤖 Commands and Tools\n\nError parsing log content.\n\n## 🤖 Reasoning\n\nUnable to parse reasoning from log.\n\n"; } - } - - function parseFirewallLogLine(line) { - - const trimmed = line.trim(); - - if (!trimmed || trimmed.startsWith("#")) { - - return null; - + function formatCodexToolCall(server, toolName, params, response, statusIcon) { + const totalTokens = estimateTokens(params) + estimateTokens(response); + let metadata = ""; + if (totalTokens > 0) { + metadata = `~${totalTokens}t`; } - - const fields = trimmed.match(/(?:[^\s"]+|"[^"]*")+/g); - - if (!fields || fields.length < 10) { - - return null; - + const summary = `${server}::${toolName}`; + const sections = []; + if (params && params.trim()) { + sections.push({ + label: "Parameters", + content: params, + language: "json", + }); } - - const timestamp = fields[0]; - - if (!/^\d+(\.\d+)?$/.test(timestamp)) { - - return null; - + if (response && response.trim()) { + sections.push({ + label: "Response", + content: response, + language: "json", + }); } - - return { - - timestamp, - - clientIpPort: fields[1], - - domain: fields[2], - - destIpPort: fields[3], - - proto: fields[4], - - method: fields[5], - - status: fields[6], - - decision: fields[7], - - url: fields[8], - - userAgent: fields[9]?.replace(/^"|"$/g, "") || "-", - - }; - + return formatToolCallAsDetails({ + summary, + statusIcon, + metadata, + sections, + }); } - - function isRequestAllowed(decision, status) { - - const statusCode = parseInt(status, 10); - - if (statusCode === 200 || statusCode === 206 || statusCode === 304) { - - return true; - - } - - if (decision.includes("TCP_TUNNEL") || decision.includes("TCP_HIT") || decision.includes("TCP_MISS")) { - - return true; - - } - - if (decision.includes("NONE_NONE") || decision.includes("TCP_DENIED") || statusCode === 403 || statusCode === 407) { - - return false; - + function formatCodexBashCall(command, response, statusIcon) { + const totalTokens = estimateTokens(command) + estimateTokens(response); + let metadata = ""; + if (totalTokens > 0) { + metadata = `~${totalTokens}t`; } - - return false; - - } - - function generateFirewallSummary(analysis) { - - const { totalRequests, deniedRequests, deniedDomains, requestsByDomain } = analysis; - - let summary = "### 🔥 Firewall Blocked Requests\n\n"; - - const validDeniedDomains = deniedDomains.filter(domain => domain !== "-"); - - const validDeniedRequests = validDeniedDomains.reduce((sum, domain) => sum + (requestsByDomain.get(domain)?.denied || 0), 0); - - if (validDeniedRequests > 0) { - - summary += `**${validDeniedRequests}** request${validDeniedRequests !== 1 ? "s" : ""} blocked across **${validDeniedDomains.length}** unique domain${validDeniedDomains.length !== 1 ? "s" : ""}`; - - summary += ` (${totalRequests > 0 ? Math.round((validDeniedRequests / totalRequests) * 100) : 0}% of total traffic)\n\n`; - - summary += "
\n"; - - summary += "🚫 Blocked Domains (click to expand)\n\n"; - - summary += "| Domain | Blocked Requests |\n"; - - summary += "|--------|------------------|\n"; - - for (const domain of validDeniedDomains) { - - const stats = requestsByDomain.get(domain); - - summary += `| ${domain} | ${stats.denied} |\n`; - - } - - summary += "\n
\n\n"; - - } else { - - summary += "✅ **No blocked requests detected**\n\n"; - - if (totalRequests > 0) { - - summary += `All ${totalRequests} request${totalRequests !== 1 ? "s" : ""} were allowed through the firewall.\n\n`; - - } else { - - summary += "No firewall activity detected.\n\n"; - - } - + const summary = `bash: ${truncateString(command, 60)}`; + const sections = []; + sections.push({ + label: "Command", + content: command, + language: "bash", + }); + if (response && response.trim()) { + sections.push({ + label: "Output", + content: response, + }); } - - return summary; - + return formatToolCallAsDetails({ + summary, + statusIcon, + metadata, + sections, + }); } - if (typeof module !== "undefined" && module.exports) { - module.exports = { - - parseFirewallLogLine, - - isRequestAllowed, - - generateFirewallSummary, - - main, - + parseCodexLog, + formatCodexToolCall, + formatCodexBashCall, + extractMCPInitialization, }; - - } - - const isDirectExecution = - - typeof module === "undefined" || (typeof require !== "undefined" && typeof require.main !== "undefined" && require.main === module); - - if (isDirectExecution) { - - main(); - } - + main(); - name: Upload Agent Stdio if: always() uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 @@ -5480,8 +4930,8 @@ jobs: if: always() uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: - GH_AW_AGENT_OUTPUT: /tmp/gh-aw/.agent/logs/ - GH_AW_ERROR_PATTERNS: "[{\"id\":\"\",\"pattern\":\"::(error)(?:\\\\s+[^:]*)?::(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"GitHub Actions workflow command - error\"},{\"id\":\"\",\"pattern\":\"::(warning)(?:\\\\s+[^:]*)?::(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"GitHub Actions workflow command - warning\"},{\"id\":\"\",\"pattern\":\"::(notice)(?:\\\\s+[^:]*)?::(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"GitHub Actions workflow command - notice\"},{\"id\":\"\",\"pattern\":\"(ERROR|Error):\\\\s+(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"Generic ERROR messages\"},{\"id\":\"\",\"pattern\":\"(WARNING|Warning):\\\\s+(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"Generic WARNING messages\"},{\"id\":\"\",\"pattern\":\"(\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}\\\\.\\\\d{3}Z)\\\\s+\\\\[(ERROR)\\\\]\\\\s+(.+)\",\"level_group\":2,\"message_group\":3,\"description\":\"Copilot CLI timestamped ERROR messages\"},{\"id\":\"\",\"pattern\":\"(\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}\\\\.\\\\d{3}Z)\\\\s+\\\\[(WARN|WARNING)\\\\]\\\\s+(.+)\",\"level_group\":2,\"message_group\":3,\"description\":\"Copilot CLI timestamped WARNING messages\"},{\"id\":\"\",\"pattern\":\"\\\\[(\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}\\\\.\\\\d{3}Z)\\\\]\\\\s+(CRITICAL|ERROR):\\\\s+(.+)\",\"level_group\":2,\"message_group\":3,\"description\":\"Copilot CLI bracketed critical/error messages with timestamp\"},{\"id\":\"\",\"pattern\":\"\\\\[(\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}\\\\.\\\\d{3}Z)\\\\]\\\\s+(WARNING):\\\\s+(.+)\",\"level_group\":2,\"message_group\":3,\"description\":\"Copilot CLI bracketed warning messages with timestamp\"},{\"id\":\"\",\"pattern\":\"✗\\\\s+(.+)\",\"level_group\":0,\"message_group\":1,\"description\":\"Copilot CLI failed command indicator\"},{\"id\":\"\",\"pattern\":\"(?:command not found|not found):\\\\s*(.+)|(.+):\\\\s*(?:command not found|not found)\",\"level_group\":0,\"message_group\":0,\"description\":\"Shell command not found error\"},{\"id\":\"\",\"pattern\":\"Cannot find module\\\\s+['\\\"](.+)['\\\"]\",\"level_group\":0,\"message_group\":1,\"description\":\"Node.js module not found error\"},{\"id\":\"\",\"pattern\":\"Permission denied and could not request permission from user\",\"level_group\":0,\"message_group\":0,\"description\":\"Copilot CLI permission denied warning (user interaction required)\"},{\"id\":\"\",\"pattern\":\"\\\\berror\\\\b.*permission.*denied\",\"level_group\":0,\"message_group\":0,\"description\":\"Permission denied error (requires error context)\"},{\"id\":\"\",\"pattern\":\"\\\\berror\\\\b.*unauthorized\",\"level_group\":0,\"message_group\":0,\"description\":\"Unauthorized access error (requires error context)\"},{\"id\":\"\",\"pattern\":\"\\\\berror\\\\b.*forbidden\",\"level_group\":0,\"message_group\":0,\"description\":\"Forbidden access error (requires error context)\"}]" + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log + GH_AW_ERROR_PATTERNS: "[{\"id\":\"\",\"pattern\":\"::(error)(?:\\\\s+[^:]*)?::(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"GitHub Actions workflow command - error\"},{\"id\":\"\",\"pattern\":\"::(warning)(?:\\\\s+[^:]*)?::(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"GitHub Actions workflow command - warning\"},{\"id\":\"\",\"pattern\":\"::(notice)(?:\\\\s+[^:]*)?::(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"GitHub Actions workflow command - notice\"},{\"id\":\"\",\"pattern\":\"(ERROR|Error):\\\\s+(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"Generic ERROR messages\"},{\"id\":\"\",\"pattern\":\"(WARNING|Warning):\\\\s+(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"Generic WARNING messages\"},{\"id\":\"\",\"pattern\":\"(\\\\d{4}-\\\\d{2}-\\\\d{2}T[\\\\d:.]+Z)\\\\s+(ERROR)\\\\s+(.+)\",\"level_group\":2,\"message_group\":3,\"description\":\"Codex ERROR messages with timestamp\"},{\"id\":\"\",\"pattern\":\"(\\\\d{4}-\\\\d{2}-\\\\d{2}T[\\\\d:.]+Z)\\\\s+(WARN|WARNING)\\\\s+(.+)\",\"level_group\":2,\"message_group\":3,\"description\":\"Codex warning messages with timestamp\"}]" with: script: | function main() { @@ -7066,7 +6516,8 @@ jobs: GH_AW_UPDATE_TITLE: false GH_AW_UPDATE_BODY: true GH_AW_WORKFLOW_NAME: "Changeset Generator" - GH_AW_ENGINE_ID: "copilot" + GH_AW_ENGINE_ID: "codex" + GH_AW_ENGINE_MODEL: "gpt-5-mini" with: github-token: ${{ steps.app-token.outputs.token }} script: | diff --git a/.github/workflows/changeset.md b/.github/workflows/changeset.md index 117ff59684c..5eb6afd6dd2 100644 --- a/.github/workflows/changeset.md +++ b/.github/workflows/changeset.md @@ -12,7 +12,10 @@ permissions: contents: read pull-requests: read issues: read -engine: copilot +engine: + id: codex + model: gpt-5-mini +strict: false # Required: codex engine doesn't support network firewall safe-outputs: push-to-pull-request-branch: commit-title-suffix: " [skip-ci]" @@ -25,7 +28,6 @@ network: allowed: - defaults - node - firewall: true tools: bash: - "*" diff --git a/docs/src/content/docs/labs.mdx b/docs/src/content/docs/labs.mdx index 826900eee16..42a800165cd 100644 --- a/docs/src/content/docs/labs.mdx +++ b/docs/src/content/docs/labs.mdx @@ -17,7 +17,7 @@ These are experimental agentic workflows used by the GitHub Next team to learn, | [Blog Auditor](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/blog-auditor.md) | claude | [![Blog Auditor](https://github.com/githubnext/gh-aw/actions/workflows/blog-auditor.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/blog-auditor.lock.yml) | `0 12 * * 3` | - | | [Brave Web Search Agent](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/brave.md) | copilot | [![Brave Web Search Agent](https://github.com/githubnext/gh-aw/actions/workflows/brave.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/brave.lock.yml) | - | `/brave` | | [Breaking Change Checker](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/breaking-change-checker.md) | copilot | [![Breaking Change Checker](https://github.com/githubnext/gh-aw/actions/workflows/breaking-change-checker.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/breaking-change-checker.lock.yml) | `0 14 * * 1-5` | - | -| [Changeset Generator](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/changeset.md) | copilot | [![Changeset Generator](https://github.com/githubnext/gh-aw/actions/workflows/changeset.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/changeset.lock.yml) | - | - | +| [Changeset Generator](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/changeset.md) | codex | [![Changeset Generator](https://github.com/githubnext/gh-aw/actions/workflows/changeset.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/changeset.lock.yml) | - | - | | [CI Failure Doctor](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/ci-doctor.md) | copilot | [![CI Failure Doctor](https://github.com/githubnext/gh-aw/actions/workflows/ci-doctor.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/ci-doctor.lock.yml) | - | - | | [Claude Command Processor - /cloclo](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/cloclo.md) | claude | [![Claude Command Processor - /cloclo](https://github.com/githubnext/gh-aw/actions/workflows/cloclo.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/cloclo.lock.yml) | - | `/cloclo` | | [CLI Consistency Checker](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/cli-consistency-checker.md) | copilot | [![CLI Consistency Checker](https://github.com/githubnext/gh-aw/actions/workflows/cli-consistency-checker.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/cli-consistency-checker.lock.yml) | `0 13 * * 1-5` | - |