Issue Monster #7425
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"8c3c7292507fc1b84ce159cb5604115861c3c76d00a9671ce2e401d8f527508e","body_hash":"640c4bdbb7e81ce552cd2e1106b1bf2cc175c6eb756fdf28930dc4b089af61f2","strict":true,"agent_id":"pi","agent_model":"copilot/gpt-5.4","engine_versions":{"pi":"0.84.2"}} | |
| # gh-aw-manifest: {"version":1,"secrets":["GH_AW_AGENT_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.28.1","digest":"sha256:5e3f6ee27eeae07195838b97ac4aa2f8aea42a7c55f1c0d3e17d8e88e294ad0d","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.28.1@sha256:5e3f6ee27eeae07195838b97ac4aa2f8aea42a7c55f1c0d3e17d8e88e294ad0d"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.1","digest":"sha256:288e7d2a12d5b430500d739f9c16e20bb1ed51b91f986f3f3eccde189f489f5c","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.1@sha256:288e7d2a12d5b430500d739f9c16e20bb1ed51b91f986f3f3eccde189f489f5c"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.28.1","digest":"sha256:f931e5e1e13f765605d03ef9511fc755d779a51b76581ea14586e9871506a610","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.28.1@sha256:f931e5e1e13f765605d03ef9511fc755d779a51b76581ea14586e9871506a610"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.28.1","digest":"sha256:9d428af47899bf18ef2d5618075777d76ef344c91e76c1f44ec1aaa0ee347e5f","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.28.1@sha256:9d428af47899bf18ef2d5618075777d76ef344c91e76c1f44ec1aaa0ee347e5f"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.9","digest":"sha256:e5a1569aeaf41820fa7bdee3e94468cae448133cdbf00119ad24f5b74db1ab9f","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.9@sha256:e5a1569aeaf41820fa7bdee3e94468cae448133cdbf00119ad24f5b74db1ab9f"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196","pinned_image":"ghcr.io/github/gh-aw-node@sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196"},{"image":"ghcr.io/github/github-mcp-server:v1.9.0","digest":"sha256:881b53d6f75f69bdbc1b5b10fc2f1361717c19054143b3a8529fb5c32061a50e","pinned_image":"ghcr.io/github/github-mcp-server:v1.9.0@sha256:881b53d6f75f69bdbc1b5b10fc2f1361717c19054143b3a8529fb5c32061a50e"}]} | |
| # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md | |
| # | |
| # ___ _ _ | |
| # / _ \ | | (_) | |
| # | |_| | __ _ ___ _ __ | |_ _ ___ | |
| # | _ |/ _` |/ _ \ '_ \| __| |/ __| | |
| # | | | | (_| | __/ | | | |_| | (__ | |
| # \_| |_/\__, |\___|_| |_|\__|_|\___| | |
| # __/ | | |
| # _ _ |___/ | |
| # | | | | / _| | | |
| # | | | | ___ _ __ _ __| |_| | _____ ____ | |
| # | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| | |
| # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ | |
| # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ | |
| # | |
| # | |
| # To update this file, edit the corresponding .md file and run: | |
| # gh aw compile | |
| # Not all edits will cause changes to this file. | |
| # | |
| # For more information: https://github.github.com/gh-aw/introduction/overview/ | |
| # | |
| # The Cookie Monster of issues - assigns issues to Copilot coding agent one at a time | |
| # | |
| # Resolved workflow manifest: | |
| # Imports: | |
| # - shared/activation-app.md | |
| # - shared/github-guard-policy.md | |
| # - shared/mcp-pagination.md | |
| # - shared/otlp.md | |
| # | |
| # Secrets used: | |
| # - GH_AW_AGENT_TOKEN | |
| # - GH_AW_GITHUB_MCP_SERVER_TOKEN | |
| # - GH_AW_GITHUB_TOKEN | |
| # - GH_AW_OTEL_GRAFANA_AUTHORIZATION | |
| # - GH_AW_OTEL_GRAFANA_ENDPOINT | |
| # - GH_AW_OTEL_SENTRY_AUTHORIZATION | |
| # - GH_AW_OTEL_SENTRY_ENDPOINT | |
| # - GITHUB_TOKEN | |
| # | |
| # Custom actions used: | |
| # - actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| # - actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| # - actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) | |
| # - actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| # | |
| # Container images used: | |
| # - ghcr.io/github/gh-aw-firewall/agent:0.28.1@sha256:5e3f6ee27eeae07195838b97ac4aa2f8aea42a7c55f1c0d3e17d8e88e294ad0d | |
| # - ghcr.io/github/gh-aw-firewall/api-proxy:0.28.1@sha256:288e7d2a12d5b430500d739f9c16e20bb1ed51b91f986f3f3eccde189f489f5c | |
| # - ghcr.io/github/gh-aw-firewall/cli-proxy:0.28.1@sha256:f931e5e1e13f765605d03ef9511fc755d779a51b76581ea14586e9871506a610 | |
| # - ghcr.io/github/gh-aw-firewall/squid:0.28.1@sha256:9d428af47899bf18ef2d5618075777d76ef344c91e76c1f44ec1aaa0ee347e5f | |
| # - ghcr.io/github/gh-aw-mcpg:v0.4.9@sha256:e5a1569aeaf41820fa7bdee3e94468cae448133cdbf00119ad24f5b74db1ab9f | |
| # - ghcr.io/github/gh-aw-node@sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196 | |
| # - ghcr.io/github/github-mcp-server:v1.9.0@sha256:881b53d6f75f69bdbc1b5b10fc2f1361717c19054143b3a8529fb5c32061a50e | |
| name: "Issue Monster" | |
| on: | |
| # permissions: # Permissions applied to pre-activation job | |
| # issues: read | |
| # pull-requests: read | |
| schedule: | |
| - cron: "5/30 * * * *" # Friendly format: every 30m (scattered) | |
| # skip-if-check-failing: # Skip-if-check-failing processed as check status gate in pre-activation job | |
| # allow-pending: true | |
| # include: | |
| # - build | |
| # - test | |
| # - lint-go | |
| # - lint-js | |
| # skip-if-match: # Skip-if-match processed as search check in pre-activation job | |
| # max: 5 | |
| # query: is:pr is:open is:draft author:app/copilot-swe-agent | |
| # skip-if-no-match: is:issue is:open # Skip-if-no-match processed as search check in pre-activation job | |
| # steps: # Steps injected into pre-activation job | |
| # - id: search | |
| # name: Search for candidate issues | |
| # uses: actions/github-script@v9.0.0 | |
| # with: | |
| # script: | | |
| # const { owner, repo } = context.repo; | |
| # const MAX_ISSUES_WITH_BODY_CONTEXT = 8; | |
| # const BODY_SNIPPET_MAX_LENGTH = 600; | |
| # | |
| # try { | |
| # // Check for recent rate-limited PRs to avoid scheduling more work during rate limiting | |
| # core.info('Checking for recent rate-limited PRs...'); | |
| # const rateLimitCheckDate = new Date(); | |
| # rateLimitCheckDate.setHours(rateLimitCheckDate.getHours() - 1); // Check last hour | |
| # // Format as YYYY-MM-DDTHH:MM:SS for GitHub search API | |
| # const rateLimitCheckISO = rateLimitCheckDate.toISOString().split('.')[0] + 'Z'; | |
| # | |
| # const recentPRsQuery = `is:pr author:app/copilot-swe-agent created:>${rateLimitCheckISO} repo:${owner}/${repo}`; | |
| # const recentPRsResponse = await github.rest.search.issuesAndPullRequests({ | |
| # q: recentPRsQuery, | |
| # per_page: 10, | |
| # sort: 'created', | |
| # order: 'desc' | |
| # }); | |
| # | |
| # core.info(`Found ${recentPRsResponse.data.total_count} recent Copilot PRs to check for rate limiting`); | |
| # | |
| # // Check if any recent PRs have rate limit indicators | |
| # let rateLimitDetected = false; | |
| # for (const pr of recentPRsResponse.data.items) { | |
| # try { | |
| # const prTimelineQuery = ` | |
| # query($owner: String!, $repo: String!, $number: Int!) { | |
| # repository(owner: $owner, name: $repo) { | |
| # pullRequest(number: $number) { | |
| # timelineItems(first: 50, itemTypes: [ISSUE_COMMENT]) { | |
| # nodes { | |
| # __typename | |
| # ... on IssueComment { | |
| # body | |
| # createdAt | |
| # } | |
| # } | |
| # } | |
| # } | |
| # } | |
| # } | |
| # `; | |
| # | |
| # const prTimelineResult = await github.graphql(prTimelineQuery, { | |
| # owner, | |
| # repo, | |
| # number: pr.number | |
| # }); | |
| # | |
| # const comments = prTimelineResult?.repository?.pullRequest?.timelineItems?.nodes || []; | |
| # const rateLimitPattern = /rate limit|API rate limit|secondary rate limit|abuse detection|\b429\b|too many requests/i; | |
| # | |
| # for (const comment of comments) { | |
| # if (comment.body && rateLimitPattern.test(comment.body)) { | |
| # core.warning(`Rate limiting detected in PR #${pr.number}: ${comment.body.substring(0, 200)}`); | |
| # rateLimitDetected = true; | |
| # break; | |
| # } | |
| # } | |
| # | |
| # if (rateLimitDetected) break; | |
| # } catch (error) { | |
| # core.warning(`Could not check PR #${pr.number} for rate limiting: ${error.message}`); | |
| # } | |
| # } | |
| # | |
| # if (rateLimitDetected) { | |
| # core.warning('🛑 Rate limiting detected in recent PRs. Skipping issue assignment to prevent further rate limit issues.'); | |
| # core.setOutput('issue_count', 0); | |
| # core.setOutput('issue_numbers', ''); | |
| # core.setOutput('issue_list', ''); | |
| # core.setOutput('issue_context', ''); | |
| # core.setOutput('retry_blocked_list', ''); | |
| # core.setOutput('has_issues', 'false'); | |
| # return; | |
| # } | |
| # | |
| # core.info('✓ No rate limiting detected. Proceeding with issue search.'); | |
| # | |
| # // Labels that indicate an issue should NOT be auto-assigned | |
| # const excludeLabels = [ | |
| # 'wontfix', | |
| # 'duplicate', | |
| # 'invalid', | |
| # 'question', | |
| # 'discussion', | |
| # 'needs-discussion', | |
| # 'blocked', | |
| # 'on-hold', | |
| # 'waiting-for-feedback', | |
| # 'needs-more-info', | |
| # 'no-bot', | |
| # 'no-campaign', | |
| # // Topics where repeated Copilot attempts were closed without merging. | |
| # // A maintainer must remove this label before a new attempt is dispatched. | |
| # 'copilot-retry-blocked' | |
| # ]; | |
| # | |
| # // Labels that indicate an issue is a GOOD candidate for auto-assignment | |
| # const priorityLabels = [ | |
| # 'community', | |
| # 'good first issue', | |
| # 'good-first-issue', | |
| # 'bug', | |
| # 'enhancement', | |
| # 'feature', | |
| # 'documentation', | |
| # 'tech-debt', | |
| # 'refactoring', | |
| # 'performance', | |
| # 'security' | |
| # ]; | |
| # | |
| # // Search for open issues with "cookie" label and without excluded labels | |
| # // The "cookie" label indicates issues that are approved work queue items from automated workflows | |
| # const query = `is:issue is:open repo:${owner}/${repo} label:cookie -label:"${excludeLabels.join('" -label:"')}"`; | |
| # core.info(`Searching: ${query}`); | |
| # const response = await github.rest.search.issuesAndPullRequests({ | |
| # q: query, | |
| # per_page: 100, | |
| # sort: 'created', | |
| # order: 'desc' | |
| # }); | |
| # core.info(`Found ${response.data.total_count} total issues matching basic criteria`); | |
| # | |
| # // Fetch full details for each issue to get labels, assignees, sub-issues, and linked PRs | |
| # // Track integrity-filtered issues to emit a diagnostic summary | |
| # const integrityFilteredIssues = []; | |
| # const issuesWithDetails = (await Promise.all( | |
| # response.data.items.map(async (issue) => { | |
| # // Fetch full issue details — some issues may be blocked by integrity policy | |
| # let fullIssue; | |
| # try { | |
| # fullIssue = await github.rest.issues.get({ | |
| # owner, | |
| # repo, | |
| # issue_number: issue.number | |
| # }); | |
| # } catch (fetchError) { | |
| # // Integrity-filtered issues (403/451) or other transient errors should be | |
| # // skipped individually rather than failing the entire batch | |
| # const status = fetchError.status || fetchError.response?.status; | |
| # // 403 = Forbidden (integrity policy), 451 = Unavailable For Legal Reasons | |
| # const isIntegrityBlock = status === 403 || status === 451 || | |
| # /\bintegrity\b/i.test(fetchError.message || ''); | |
| # const errorSummary = (fetchError.message || String(fetchError)).slice(0, 120); | |
| # if (isIntegrityBlock) { | |
| # integrityFilteredIssues.push(issue.number); | |
| # core.warning(`⚠️ Skipping issue #${issue.number}: blocked by integrity policy (HTTP ${status || 'unknown'}): ${errorSummary}`); | |
| # } else { | |
| # core.warning(`⚠️ Skipping issue #${issue.number}: could not fetch details (HTTP ${status || 'unknown'}): ${errorSummary}`); | |
| # } | |
| # return null; | |
| # } | |
| # | |
| # // Check if this issue has sub-issues and linked PRs using GraphQL | |
| # let subIssuesCount = 0; | |
| # let linkedPRs = []; | |
| # try { | |
| # const issueDetailsQuery = ` | |
| # query($owner: String!, $repo: String!, $number: Int!) { | |
| # repository(owner: $owner, name: $repo) { | |
| # issue(number: $number) { | |
| # subIssues { | |
| # totalCount | |
| # } | |
| # timelineItems(first: 100, itemTypes: [CROSS_REFERENCED_EVENT]) { | |
| # nodes { | |
| # ... on CrossReferencedEvent { | |
| # source { | |
| # __typename | |
| # ... on PullRequest { | |
| # number | |
| # state | |
| # isDraft | |
| # author { | |
| # login | |
| # } | |
| # } | |
| # } | |
| # } | |
| # } | |
| # } | |
| # } | |
| # } | |
| # } | |
| # `; | |
| # const issueDetailsResult = await github.graphql(issueDetailsQuery, { | |
| # owner, | |
| # repo, | |
| # number: issue.number | |
| # }); | |
| # | |
| # subIssuesCount = issueDetailsResult?.repository?.issue?.subIssues?.totalCount || 0; | |
| # | |
| # // Extract linked PRs from timeline | |
| # const timelineItems = issueDetailsResult?.repository?.issue?.timelineItems?.nodes || []; | |
| # linkedPRs = timelineItems | |
| # .filter(item => item?.source?.__typename === 'PullRequest') | |
| # .map(item => ({ | |
| # number: item.source.number, | |
| # state: item.source.state, | |
| # isDraft: item.source.isDraft, | |
| # author: item.source.author?.login | |
| # })); | |
| # | |
| # core.info(`Issue #${issue.number} has ${linkedPRs.length} linked PR(s)`); | |
| # } catch (error) { | |
| # // If GraphQL query fails, continue with defaults | |
| # core.warning(`Could not check details for #${issue.number}: ${error.message}`); | |
| # } | |
| # | |
| # return { | |
| # ...fullIssue.data, | |
| # subIssuesCount, | |
| # linkedPRs | |
| # }; | |
| # }) | |
| # )).filter(Boolean); // Remove null entries (integrity-filtered or otherwise skipped) | |
| # | |
| # // Emit diagnostic summary for integrity-filtered issues | |
| # if (integrityFilteredIssues.length > 0) { | |
| # core.warning(`🛡️ Integrity filter diagnostic: ${integrityFilteredIssues.length} issue(s) were skipped due to integrity policy: #${integrityFilteredIssues.join(', #')}. These issues will be excluded from this run.`); | |
| # } | |
| # | |
| # // Pre-flight retry-blocked check: build a map of topics that Copilot already | |
| # // attempted and had closed without merging. Repeated attempts on the same topic | |
| # // burn a full agent session each time and need a human checkpoint first. | |
| # const RETRY_BLOCK_THRESHOLD = 2; | |
| # const MIN_TOPIC_LENGTH = 20; | |
| # // Normalize a title into a comparable topic key: drop bracketed prefixes | |
| # // (e.g. "[WIP] ", "[copilot-opt] "), lowercase, and strip punctuation. | |
| # const normalizeTopic = (title) => (title || '') | |
| # .replace(/^\s*(\[[^\]]*\]\s*)+/, '') | |
| # .toLowerCase() | |
| # .replace(/[^a-z0-9]+/g, ' ') | |
| # .trim(); | |
| # const closedTopicCounts = new Map(); | |
| # try { | |
| # const closedPRQuery = `is:pr is:closed is:unmerged author:app/copilot-swe-agent repo:${owner}/${repo}`; | |
| # const closedPRResponse = await github.rest.search.issuesAndPullRequests({ | |
| # q: closedPRQuery, | |
| # per_page: 100, | |
| # sort: 'created', | |
| # order: 'desc' | |
| # }); | |
| # for (const pr of closedPRResponse.data.items) { | |
| # const topic = normalizeTopic(pr.title); | |
| # if (topic.length < MIN_TOPIC_LENGTH) continue; | |
| # const entry = closedTopicCounts.get(topic) || { count: 0, prs: [] }; | |
| # entry.count += 1; | |
| # entry.prs.push(pr.number); | |
| # closedTopicCounts.set(topic, entry); | |
| # } | |
| # const blockedTopics = [...closedTopicCounts.entries()].filter(([, v]) => v.count >= RETRY_BLOCK_THRESHOLD); | |
| # core.info(`Retry pre-flight: ${closedTopicCounts.size} closed-unmerged Copilot topics, ${blockedTopics.length} at or above the retry-block threshold (${RETRY_BLOCK_THRESHOLD})`); | |
| # } catch (error) { | |
| # // A failed pre-flight check must not block the run; fall back to no topic data. | |
| # core.warning(`Could not build retry-blocked topic map: ${error.message}`); | |
| # } | |
| # // Returns the blocked topic entry for an issue title, or null when not blocked. | |
| # const findRetryBlock = (title) => { | |
| # const topic = normalizeTopic(title); | |
| # if (topic.length < MIN_TOPIC_LENGTH) return null; | |
| # for (const [closedTopic, entry] of closedTopicCounts) { | |
| # if (entry.count < RETRY_BLOCK_THRESHOLD) continue; | |
| # if (topic === closedTopic || topic.includes(closedTopic) || closedTopic.includes(topic)) { | |
| # return entry; | |
| # } | |
| # } | |
| # return null; | |
| # }; | |
| # | |
| # // Filter and score issues | |
| # const retryBlockedIssues = []; | |
| # const scoredIssues = issuesWithDetails | |
| # .filter(issue => { | |
| # // Exclude issues that already have assignees | |
| # if (issue.assignees && issue.assignees.length > 0) { | |
| # core.info(`Skipping #${issue.number}: already has assignees`); | |
| # return false; | |
| # } | |
| # | |
| # // Exclude issues with excluded labels (double check) | |
| # const issueLabels = issue.labels.map(l => l.name.toLowerCase()); | |
| # if (issueLabels.some(label => excludeLabels.map(l => l.toLowerCase()).includes(label))) { | |
| # core.info(`Skipping #${issue.number}: has excluded label`); | |
| # return false; | |
| # } | |
| # | |
| # // Exclude issues with campaign labels (campaign:*) | |
| # // Campaign items are managed by campaign orchestrators | |
| # if (issueLabels.some(label => label.startsWith('campaign:'))) { | |
| # core.info(`Skipping #${issue.number}: has campaign label (managed by campaign orchestrator)`); | |
| # return false; | |
| # } | |
| # | |
| # // Exclude issues that have sub-issues (parent/organizing issues) | |
| # if (issue.subIssuesCount > 0) { | |
| # core.info(`Skipping #${issue.number}: has ${issue.subIssuesCount} sub-issue(s) - parent issues are used for organizing, not tasks`); | |
| # return false; | |
| # } | |
| # | |
| # // Exclude issues with closed PRs (treat as complete) | |
| # const closedPRs = issue.linkedPRs?.filter(pr => pr.state === 'CLOSED' || pr.state === 'MERGED') || []; | |
| # if (closedPRs.length > 0) { | |
| # core.info(`Skipping #${issue.number}: has ${closedPRs.length} closed/merged PR(s) - treating as complete`); | |
| # return false; | |
| # } | |
| # | |
| # // Exclude issues with open PRs from Copilot coding agent | |
| # const openCopilotPRs = issue.linkedPRs?.filter(pr => | |
| # pr.state === 'OPEN' && | |
| # (pr.author === 'copilot-swe-agent' || pr.author?.includes('copilot')) | |
| # ) || []; | |
| # if (openCopilotPRs.length > 0) { | |
| # core.info(`Skipping #${issue.number}: has ${openCopilotPRs.length} open PR(s) from Copilot - already being worked on`); | |
| # return false; | |
| # } | |
| # | |
| # // Block topics that Copilot already attempted twice or more without a merge. | |
| # // These require human review before another agent session is spent. | |
| # const retryBlock = findRetryBlock(issue.title); | |
| # if (retryBlock) { | |
| # core.warning(`🛑 Skipping #${issue.number}: retry-blocked topic — ${retryBlock.count} prior Copilot PR(s) closed without merging (#${retryBlock.prs.join(', #')}). Human review required.`); | |
| # retryBlockedIssues.push({ number: issue.number, count: retryBlock.count, prs: retryBlock.prs }); | |
| # return false; | |
| # } | |
| # | |
| # return true; | |
| # }) | |
| # .map(issue => { | |
| # const issueLabels = issue.labels.map(l => l.name.toLowerCase()); | |
| # let score = 0; | |
| # | |
| # // Score based on priority labels (higher score = higher priority) | |
| # // Community issues always get the highest priority — these are | |
| # // requests from external contributors and should be addressed first. | |
| # if (issueLabels.includes('community')) { | |
| # score += 60; | |
| # } | |
| # if (issueLabels.includes('good first issue') || issueLabels.includes('good-first-issue')) { | |
| # score += 50; | |
| # } | |
| # if (issueLabels.includes('bug')) { | |
| # score += 40; | |
| # } | |
| # if (issueLabels.includes('security')) { | |
| # score += 45; | |
| # } | |
| # if (issueLabels.includes('documentation')) { | |
| # score += 35; | |
| # } | |
| # if (issueLabels.includes('enhancement') || issueLabels.includes('feature')) { | |
| # score += 30; | |
| # } | |
| # if (issueLabels.includes('performance')) { | |
| # score += 25; | |
| # } | |
| # if (issueLabels.includes('tech-debt') || issueLabels.includes('refactoring')) { | |
| # score += 20; | |
| # } | |
| # | |
| # // Bonus for issues with clear labels (any priority label) | |
| # if (issueLabels.some(label => priorityLabels.map(l => l.toLowerCase()).includes(label))) { | |
| # score += 10; | |
| # } | |
| # | |
| # // Age bonus: older issues get slight priority (days old / 10) | |
| # const ageInDays = Math.floor((Date.now() - new Date(issue.created_at)) / (1000 * 60 * 60 * 24)); | |
| # score += Math.min(ageInDays / 10, 20); // Cap age bonus at 20 points | |
| # | |
| # return { | |
| # number: issue.number, | |
| # title: issue.title, | |
| # labels: issue.labels.map(l => l.name), | |
| # body: issue.body, | |
| # created_at: issue.created_at, | |
| # score | |
| # }; | |
| # }) | |
| # .sort((a, b) => b.score - a.score); // Sort by score descending | |
| # | |
| # // Format output | |
| # const issueList = scoredIssues.map(i => { | |
| # const labelStr = i.labels.length > 0 ? ` [${i.labels.join(', ')}]` : ''; | |
| # return `#${i.number}: ${i.title}${labelStr} (score: ${i.score.toFixed(1)})`; | |
| # }).join('\n'); | |
| # | |
| # // Pre-fetch compact body context for top candidates so the agent can | |
| # // triage without extra reads in most runs. | |
| # const issueContext = scoredIssues.slice(0, MAX_ISSUES_WITH_BODY_CONTEXT).map(i => { | |
| # const body = (i.body || '').replace(/\s+/g, ' ').trim(); | |
| # const bodySnippet = body.length > BODY_SNIPPET_MAX_LENGTH ? `${body.slice(0, BODY_SNIPPET_MAX_LENGTH)}…` : body; | |
| # const labelStr = i.labels.length > 0 ? i.labels.join(', ') : 'none'; | |
| # return `#${i.number} | score=${i.score.toFixed(1)} | labels=${labelStr}\nTitle: ${i.title}\nBody: ${bodySnippet || '(no body)'}`; | |
| # }).join('\n\n---\n\n'); | |
| # | |
| # const issueNumbers = scoredIssues.map(i => i.number).join(','); | |
| # | |
| # core.info(`Total candidate issues after filtering: ${scoredIssues.length}`); | |
| # if (scoredIssues.length > 0) { | |
| # core.info(`Top candidates:\n${issueList.split('\n').slice(0, 10).join('\n')}`); | |
| # } | |
| # | |
| # const retryBlockedList = retryBlockedIssues | |
| # .map(i => `#${i.number} | prior closed Copilot PRs: ${i.count} (#${i.prs.join(', #')})`) | |
| # .join('\n'); | |
| # if (retryBlockedIssues.length > 0) { | |
| # core.warning(`🛑 ${retryBlockedIssues.length} issue(s) were retry-blocked and excluded from assignment:\n${retryBlockedList}`); | |
| # } | |
| # | |
| # core.setOutput('issue_count', scoredIssues.length); | |
| # core.setOutput('issue_numbers', issueNumbers); | |
| # core.setOutput('issue_list', issueList); | |
| # core.setOutput('issue_context', issueContext); | |
| # core.setOutput('retry_blocked_list', retryBlockedList); | |
| # | |
| # if (scoredIssues.length === 0) { | |
| # core.info('🍽️ No suitable candidate issues - the plate is empty!'); | |
| # core.setOutput('has_issues', 'false'); | |
| # } else { | |
| # core.setOutput('has_issues', 'true'); | |
| # } | |
| # } catch (error) { | |
| # core.error(`Error searching for issues: ${error.message}`); | |
| # core.setOutput('issue_count', 0); | |
| # core.setOutput('issue_numbers', ''); | |
| # core.setOutput('issue_list', ''); | |
| # core.setOutput('issue_context', ''); | |
| # core.setOutput('retry_blocked_list', ''); | |
| # core.setOutput('has_issues', 'false'); | |
| # } | |
| workflow_dispatch: | |
| inputs: | |
| aw_context: | |
| default: "" | |
| description: "Agent caller context (used internally by Agentic Workflows)." | |
| required: false | |
| type: string | |
| permissions: {} | |
| concurrency: | |
| group: "gh-aw-${{ github.workflow }}-${{ github.event.issue.number || github.event.discussion.number || github.run_id }}" | |
| queue: max | |
| run-name: "Issue Monster" | |
| env: | |
| OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.GH_AW_OTEL_SENTRY_ENDPOINT }} | |
| OTEL_SERVICE_NAME: gh-aw.issue-monster | |
| OTEL_RESOURCE_ATTRIBUTES: 'gh-aw.workflow.name=Issue%20Monster,gh-aw.repository=${{ github.repository }},gh-aw.run.id=${{ github.run_id }},github.run_id=${{ github.run_id }},gh-aw.engine.id=pi' | |
| OTEL_EXPORTER_OTLP_HEADERS: x-sentry-auth=${{ secrets.GH_AW_OTEL_SENTRY_AUTHORIZATION }} | |
| GH_AW_OTLP_ALL_HEADERS: x-sentry-auth=${{ secrets.GH_AW_OTEL_SENTRY_AUTHORIZATION }},Authorization=${{ secrets.GH_AW_OTEL_GRAFANA_AUTHORIZATION }} | |
| GH_AW_OTLP_ENDPOINTS: '[{"url":"${{ secrets.GH_AW_OTEL_SENTRY_ENDPOINT }}","headers":"x-sentry-auth=${{ secrets.GH_AW_OTEL_SENTRY_AUTHORIZATION }}"},{"url":"${{ secrets.GH_AW_OTEL_GRAFANA_ENDPOINT }}","headers":"Authorization=${{ secrets.GH_AW_OTEL_GRAFANA_AUTHORIZATION }}"}]' | |
| jobs: | |
| activation: | |
| needs: pre_activation | |
| if: needs.pre_activation.outputs.activated == 'true' && (needs.pre_activation.outputs.has_issues == 'true') | |
| runs-on: ubuntu-slim | |
| permissions: | |
| actions: read | |
| contents: read | |
| env: | |
| GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }} | |
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | |
| outputs: | |
| comment_id: "" | |
| comment_repo: "" | |
| daily_ai_credits_exceeded: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_exceeded == 'true' }} | |
| daily_ai_credits_guardrail_status: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_guardrail_status || '' }} | |
| daily_ai_credits_threshold: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_threshold || '' }} | |
| daily_ai_credits_total_effective_tokens: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_total_effective_tokens || '' }} | |
| engine_id: ${{ steps.generate_aw_info.outputs.engine_id }} | |
| lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} | |
| model: ${{ steps.generate_aw_info.outputs.model }} | |
| oauth_token_check_failed: ${{ steps.check-oauth-tokens.outputs.oauth_token_check_failed == 'true' }} | |
| setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} | |
| setup-span-id: ${{ steps.setup.outputs.span-id }} | |
| setup-trace-id: ${{ steps.setup.outputs.trace-id }} | |
| stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} | |
| steps: | |
| - name: Checkout actions folder | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| repository: github/gh-aw | |
| sparse-checkout: | | |
| actions | |
| clean: false | |
| persist-credentials: false | |
| - name: Setup Scripts | |
| id: setup | |
| uses: ./actions/setup | |
| with: | |
| destination: ${{ runner.temp }}/gh-aw/actions | |
| job-name: ${{ github.job }} | |
| trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }} | |
| parent-span-id: ${{ needs.pre_activation.outputs.setup-parent-span-id || needs.pre_activation.outputs.setup-span-id }} | |
| safe-output-artifact-client: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | |
| env: | |
| GH_AW_SETUP_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-monster.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "0.84.2" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.1" | |
| GH_AW_INFO_ENGINE_ID: "pi" | |
| - name: Mask OTLP telemetry headers | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" | |
| - name: Generate agentic run info | |
| id: generate_aw_info | |
| env: | |
| GH_AW_INFO_ENGINE_ID: "pi" | |
| GH_AW_INFO_ENGINE_NAME: "Pi" | |
| GH_AW_INFO_MODEL: "copilot/gpt-5.4" | |
| GH_AW_INFO_VERSION: "0.84.2" | |
| GH_AW_INFO_AGENT_VERSION: "0.84.2" | |
| GH_AW_INFO_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_INFO_EXPERIMENTAL: "false" | |
| GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" | |
| GH_AW_INFO_STAGED: "false" | |
| GH_AW_INFO_ALLOWED_DOMAINS: '["*.grafana.net","*.sentry.io","defaults"]' | |
| GH_AW_INFO_FIREWALL_ENABLED: "true" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.1" | |
| GH_AW_INFO_AWMG_VERSION: "" | |
| GH_AW_INFO_FIREWALL_TYPE: "squid" | |
| GH_AW_INFO_AGENT_RUNTIME: "" | |
| GH_AW_INFO_FRONTMATTER_EMOJI: "👾" | |
| GH_AW_COMPILED_STRICT: "true" | |
| GH_AW_INFO_FEATURES: '{"gh-aw-detection":true}' | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs'); | |
| await main(core, context); | |
| - name: Restore daily AIC usage cache | |
| id: restore-daily-aic-cache | |
| if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | |
| continue-on-error: true | |
| uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| key: agentic-workflow-usage-issuemonster-${{ github.run_id }} | |
| restore-keys: agentic-workflow-usage-issuemonster- | |
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | |
| - name: Restore daily AIC usage cache (artifact fallback) | |
| id: restore-daily-aic-cache-fallback | |
| if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_RESTORE_DAILY_AIC_CACHE_HIT: ${{ steps.restore-daily-aic-cache.outputs.cache-hit }} | |
| GH_AW_RESTORE_DAILY_AIC_CACHE_MATCHED_KEY: ${{ steps.restore-daily-aic-cache.outputs.cache-matched-key }} | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/restore_aic_usage_cache_fallback.cjs'); | |
| await main(); | |
| - name: Check daily workflow token guardrail | |
| id: daily-effective-workflow-guardrail | |
| if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_WORKFLOW_ID: "issue-monster" | |
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT: ${{ github.event.inputs.aw_context || '' }} | |
| GH_AW_HAS_SLASH_COMMAND: "false" | |
| GH_AW_HAS_LABEL_COMMAND: "false" | |
| GH_AW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }} | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/check_daily_aic_workflow_guardrail.cjs'); | |
| await main(); | |
| - name: Check for OAuth tokens | |
| id: check-oauth-tokens | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/check_oauth_tokens.sh" | |
| env: | |
| GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} | |
| GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} | |
| - name: Checkout .github and .agents folders | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| sparse-checkout: | | |
| .github | |
| .agents | |
| actions/setup | |
| .claude | |
| .codex | |
| .gemini | |
| .pi | |
| sparse-checkout-cone-mode: true | |
| fetch-depth: 1 | |
| - name: Save agent config folders for base branch restoration | |
| env: | |
| GH_AW_AGENT_FOLDERS: ".agents .github .pi" | |
| GH_AW_AGENT_FILES: "AGENTS.md PI.md" | |
| run: | | |
| bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh" | |
| - name: Check workflow lock file | |
| id: check-lock-file | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_WORKFLOW_FILE: "issue-monster.lock.yml" | |
| GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}" | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs'); | |
| await main(); | |
| - name: Log runtime features | |
| if: ${{ contains(toJSON(vars), '"GH_AW_RUNTIME_FEATURES":') }} | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/log_runtime_features_summary.sh" | |
| - name: Create prompt with built-in context | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_ACTIONS_DIR: ${{ runner.temp }}/gh-aw/actions | |
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | |
| GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl | |
| GH_AW_PROMPT_CONFIG: "{\"items\":[{\"content_env\":\"GH_AW_PROMPT_CONTENT_0000\"},{\"file\":\"xpia.md\"},{\"file\":\"temp_folder_prompt.md\"},{\"file\":\"markdown.md\"},{\"file\":\"safe_outputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0001\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0002\"},{\"file\":\"mcp_cli_tools_with_safeoutputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0003\"},{\"file\":\"cli_proxy_with_safeoutputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0004\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0005\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0006\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0007\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0008\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0009\"}]}" | |
| GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | |
| GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | |
| GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | |
| GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} | |
| GH_AW_GITHUB_ACTOR: ${{ github.actor }} | |
| GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} | |
| GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} | |
| GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_CONTEXT: ${{ needs.pre_activation.outputs.issue_context }} | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_COUNT: ${{ needs.pre_activation.outputs.issue_count }} | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_LIST: ${{ needs.pre_activation.outputs.issue_list }} | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_NUMBERS: ${{ needs.pre_activation.outputs.issue_numbers }} | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_RETRY_BLOCKED_LIST: ${{ needs.pre_activation.outputs.retry_blocked_list }} | |
| GH_AW_PROMPT_CONTENT_0000: "<system>\n" | |
| GH_AW_PROMPT_CONTENT_0001: "<safe-output-tools>\nTools: add_comment(max:3), assign_to_agent(max:3), missing_tool, missing_data, noop\n" | |
| GH_AW_PROMPT_CONTENT_0002: "</safe-output-tools>\n" | |
| GH_AW_PROMPT_CONTENT_0003: "<github-context>\nThe following GitHub context information is available for this workflow:\n{{#if github.actor}}\n- **actor**: __GH_AW_GITHUB_ACTOR__\n{{/if}}\n{{#if github.repository}}\n- **repository**: __GH_AW_GITHUB_REPOSITORY__\n{{/if}}\n{{#if github.workspace}}\n- **workspace**: __GH_AW_GITHUB_WORKSPACE__\n{{/if}}\n{{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}\n- **issue-number**: #__GH_AW_EXPR_802A9F6A__\n{{/if}}\n{{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}\n- **discussion-number**: #__GH_AW_EXPR_1A3A194A__\n{{/if}}\n{{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}\n- **pull-request-number**: #__GH_AW_EXPR_463A214A__\n{{/if}}\n{{#if github.event.comment.id || github.aw.context.comment_id}}\n- **comment-id**: __GH_AW_EXPR_FF1D34CE__\n{{/if}}\n{{#if github.run_id}}\n- **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__\n{{/if}}\n</github-context>\n\n" | |
| GH_AW_PROMPT_CONTENT_0004: "</system>\n" | |
| GH_AW_PROMPT_CONTENT_0005: "{{#runtime-import .github/workflows/shared/mcp-pagination.md}}\n" | |
| GH_AW_PROMPT_CONTENT_0006: "{{#runtime-import .github/workflows/shared/github-guard-policy.md}}\n" | |
| GH_AW_PROMPT_CONTENT_0007: "{{#runtime-import .github/workflows/shared/activation-app.md}}\n" | |
| GH_AW_PROMPT_CONTENT_0008: "{{#runtime-import .github/workflows/shared/otlp.md}}\n" | |
| GH_AW_PROMPT_CONTENT_0009: "{{#runtime-import .github/workflows/issue-monster.md}}\n" | |
| with: | |
| script: | | |
| const { setupGlobals } = require(process.env.GH_AW_ACTIONS_DIR + '/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(process.env.GH_AW_ACTIONS_DIR + '/create_prompt.cjs'); | |
| await main(core); | |
| - name: Interpolate variables and render templates | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | |
| GH_AW_ENGINE_ID: "pi" | |
| GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_CONTEXT: ${{ needs.pre_activation.outputs.issue_context }} | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_COUNT: ${{ needs.pre_activation.outputs.issue_count }} | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_LIST: ${{ needs.pre_activation.outputs.issue_list }} | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_NUMBERS: ${{ needs.pre_activation.outputs.issue_numbers }} | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_RETRY_BLOCKED_LIST: ${{ needs.pre_activation.outputs.retry_blocked_list }} | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs'); | |
| await main(); | |
| - name: Substitute placeholders | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | |
| GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | |
| GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | |
| GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | |
| GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} | |
| GH_AW_GITHUB_ACTOR: ${{ github.actor }} | |
| GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} | |
| GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} | |
| GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} | |
| GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools' | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_CONTEXT: ${{ needs.pre_activation.outputs.issue_context }} | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_COUNT: ${{ needs.pre_activation.outputs.issue_count }} | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_LIST: ${{ needs.pre_activation.outputs.issue_list }} | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_NUMBERS: ${{ needs.pre_activation.outputs.issue_numbers }} | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_RETRY_BLOCKED_LIST: ${{ needs.pre_activation.outputs.retry_blocked_list }} | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs'); | |
| // Call the substitution function | |
| return await substitutePlaceholders({ | |
| file: process.env.GH_AW_PROMPT, | |
| substitutions: { | |
| GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A, | |
| GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A, | |
| GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A, | |
| GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE, | |
| GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, | |
| GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, | |
| GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, | |
| GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, | |
| GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST, | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED, | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_CONTEXT: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_CONTEXT, | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_COUNT: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_COUNT, | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_LIST: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_LIST, | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_NUMBERS: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ISSUE_NUMBERS, | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_RETRY_BLOCKED_LIST: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_RETRY_BLOCKED_LIST | |
| } | |
| }); | |
| - name: Validate prompt placeholders | |
| env: | |
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | |
| run: | | |
| bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh" | |
| - name: Print prompt | |
| env: | |
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | |
| run: | | |
| bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh" | |
| - name: Stage prompt files for artifact upload | |
| run: | | |
| mkdir -p /tmp/gh-aw/aw-prompts | |
| cp -a "${RUNNER_TEMP}/gh-aw/aw-prompts/." /tmp/gh-aw/aw-prompts/ | |
| - name: Upload activation artifact | |
| if: success() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: activation | |
| include-hidden-files: true | |
| path: | | |
| /tmp/gh-aw/aw_info.json | |
| /tmp/gh-aw/models.json | |
| /tmp/gh-aw/aw-prompts/prompt.txt | |
| /tmp/gh-aw/aw-prompts/prompt-template.txt | |
| /tmp/gh-aw/aw-prompts/prompt-import-tree.json | |
| /tmp/gh-aw/github_rate_limits.jsonl | |
| /tmp/gh-aw/base | |
| /tmp/gh-aw/.pi/agents | |
| /tmp/gh-aw/.pi/skills | |
| if-no-files-found: ignore | |
| retention-days: 1 | |
| agent: | |
| needs: activation | |
| if: needs.activation.outputs.daily_ai_credits_exceeded != 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| copilot-requests: write | |
| issues: read | |
| pull-requests: read | |
| env: | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| GH_AW_ASSETS_ALLOWED_EXTS: "" | |
| GH_AW_ASSETS_BRANCH: "" | |
| GH_AW_ASSETS_MAX_SIZE_KB: 0 | |
| GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs | |
| GH_AW_PROJECT_UTC: "-08:00" | |
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | |
| GH_AW_WORKFLOW_ID_SANITIZED: issuemonster | |
| outputs: | |
| ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} | |
| aic: ${{ steps.parse-mcp-gateway.outputs.aic }} | |
| ambient_context: ${{ steps.parse-mcp-gateway.outputs.ambient_context }} | |
| checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} | |
| effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }} | |
| has_patch: ${{ steps.collect_output.outputs.has_patch }} | |
| model: ${{ needs.activation.outputs.model }} | |
| output: ${{ steps.collect_output.outputs.output }} | |
| output_types: ${{ steps.collect_output.outputs.output_types }} | |
| setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} | |
| setup-span-id: ${{ steps.setup.outputs.span-id }} | |
| setup-trace-id: ${{ steps.setup.outputs.trace-id }} | |
| unknown_model_ai_credits: ${{ steps.parse-mcp-gateway.outputs.unknown_model_ai_credits || 'false' }} | |
| steps: | |
| - name: Checkout actions folder | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| repository: github/gh-aw | |
| sparse-checkout: | | |
| actions | |
| clean: false | |
| persist-credentials: false | |
| - name: Setup Scripts | |
| id: setup | |
| uses: ./actions/setup | |
| with: | |
| destination: ${{ runner.temp }}/gh-aw/actions | |
| job-name: ${{ github.job }} | |
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | |
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | |
| env: | |
| GH_AW_SETUP_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-monster.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "0.84.2" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.1" | |
| GH_AW_INFO_ENGINE_ID: "pi" | |
| - name: Set runtime paths | |
| id: set-runtime-paths | |
| run: | | |
| { | |
| echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl" | |
| echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" | |
| echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Mask OTLP telemetry headers | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" | |
| - name: Checkout repository | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - name: Create gh-aw temp directory | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh" | |
| - name: Configure gh CLI for GitHub Enterprise | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh" | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| - name: Download activation artifact | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: activation | |
| path: /tmp/gh-aw | |
| - name: Configure Git credentials | |
| env: | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" | |
| - name: Checkout PR branch | |
| id: checkout-pr | |
| if: | | |
| github.event.pull_request || github.event.issue.pull_request || github.event_name == 'workflow_dispatch' && fromJSON(github.event.inputs.aw_context || '{}').item_type == 'pull_request' | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); | |
| await main(); | |
| - name: Setup Node.js | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: '24' | |
| package-manager-cache: false | |
| - name: Install AWF binary | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.28.1 --rootless | |
| - name: Install Pi CLI | |
| run: npm install --ignore-scripts -g @earendil-works/pi-coding-agent@0.84.2 | |
| - name: Determine automatic lockdown mode for GitHub MCP Server | |
| id: determine-automatic-lockdown | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) | |
| env: | |
| GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} | |
| GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} | |
| GH_AW_GITHUB_MIN_INTEGRITY: 'approved' | |
| with: | |
| script: | | |
| const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs'); | |
| await determineAutomaticLockdown(github, context, core); | |
| - name: Parse integrity filter lists | |
| id: parse-guard-vars | |
| env: | |
| GH_AW_BLOCKED_USERS_VAR: ${{ vars.GH_AW_GITHUB_BLOCKED_USERS || '' }} | |
| GH_AW_TRUSTED_USERS_VAR: ${{ vars.GH_AW_GITHUB_TRUSTED_USERS || '' }} | |
| GH_AW_APPROVAL_LABELS_EXTRA: cookie,community | |
| GH_AW_APPROVAL_LABELS_VAR: ${{ vars.GH_AW_GITHUB_APPROVAL_LABELS || '' }} | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/parse_guard_list.sh" | |
| - name: Restore agent config folders from base branch | |
| if: steps.checkout-pr.outcome == 'success' | |
| env: | |
| GH_AW_AGENT_FOLDERS: ".agents .github .pi" | |
| GH_AW_AGENT_FILES: "AGENTS.md PI.md" | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh" | |
| - name: Restore inline sub-agents from activation artifact | |
| env: | |
| GH_AW_SUB_AGENT_DIR: ".pi/agents" | |
| GH_AW_SUB_AGENT_EXT: ".agent.md" | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh" | |
| - name: Restore inline skills from activation artifact | |
| env: | |
| GH_AW_SKILL_DIR: ".pi/skills" | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh" | |
| - name: Download container images | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.28.1@sha256:5e3f6ee27eeae07195838b97ac4aa2f8aea42a7c55f1c0d3e17d8e88e294ad0d ghcr.io/github/gh-aw-firewall/api-proxy:0.28.1@sha256:288e7d2a12d5b430500d739f9c16e20bb1ed51b91f986f3f3eccde189f489f5c ghcr.io/github/gh-aw-firewall/cli-proxy:0.28.1@sha256:f931e5e1e13f765605d03ef9511fc755d779a51b76581ea14586e9871506a610 ghcr.io/github/gh-aw-firewall/squid:0.28.1@sha256:9d428af47899bf18ef2d5618075777d76ef344c91e76c1f44ec1aaa0ee347e5f ghcr.io/github/gh-aw-mcpg:v0.4.9@sha256:e5a1569aeaf41820fa7bdee3e94468cae448133cdbf00119ad24f5b74db1ab9f ghcr.io/github/gh-aw-node@sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196 ghcr.io/github/github-mcp-server:v1.9.0@sha256:881b53d6f75f69bdbc1b5b10fc2f1361717c19054143b3a8529fb5c32061a50e | |
| - name: Prepare Safe Outputs Directories | |
| run: | | |
| mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" | |
| mkdir -p /tmp/gh-aw/safeoutputs | |
| mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs | |
| - name: Generate Safe Outputs Config | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw" | |
| GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}" | |
| GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":3,\"target\":\"*\"},\"assign_to_agent\":{\"allowed\":[\"copilot\"],\"ignore-if-error\":true,\"max\":3,\"target\":\"*\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}" | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/create_files.cjs'); | |
| await main(); | |
| - name: Generate Safe Outputs Tools | |
| env: | |
| GH_AW_TOOLS_META_JSON: | | |
| { | |
| "description_suffixes": { | |
| "add_comment": " CONSTRAINTS: Maximum 3 comment(s) can be added. Target: *. Supports reply_to_id for discussion threading.", | |
| "assign_to_agent": " CONSTRAINTS: Maximum 3 issue(s) can be assigned to agent." | |
| }, | |
| "repo_params": {}, | |
| "dynamic_tools": [] | |
| } | |
| GH_AW_VALIDATION_JSON: | | |
| { | |
| "add_comment": { | |
| "defaultMax": 1, | |
| "fields": { | |
| "body": { | |
| "required": true, | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 65000 | |
| }, | |
| "comment_id": { | |
| "optionalPositiveInteger": true | |
| }, | |
| "item_number": { | |
| "issueOrPRNumber": true | |
| }, | |
| "pr": { | |
| "issueOrPRNumber": true | |
| }, | |
| "pr_number": { | |
| "issueOrPRNumber": true | |
| }, | |
| "reply_to_id": { | |
| "type": "string", | |
| "maxLength": 256 | |
| }, | |
| "repo": { | |
| "type": "string", | |
| "maxLength": 256 | |
| }, | |
| "target": { | |
| "type": "string", | |
| "enum": [ | |
| "status" | |
| ] | |
| }, | |
| "temporary_id": { | |
| "type": "string", | |
| "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$" | |
| } | |
| } | |
| }, | |
| "assign_to_agent": { | |
| "defaultMax": 1, | |
| "fields": { | |
| "agent": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 128 | |
| }, | |
| "confidence": { | |
| "type": "string", | |
| "enum": [ | |
| "LOW", | |
| "MEDIUM", | |
| "HIGH" | |
| ], | |
| "x-strip-on-error": true | |
| }, | |
| "issue_number": { | |
| "issueNumberOrTemporaryId": true | |
| }, | |
| "pull_number": { | |
| "optionalPositiveInteger": true | |
| }, | |
| "pull_request_repo": { | |
| "type": "string", | |
| "maxLength": 256 | |
| }, | |
| "rationale": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 280, | |
| "x-strip-on-error": true | |
| }, | |
| "repo": { | |
| "type": "string", | |
| "maxLength": 256 | |
| }, | |
| "suggest": { | |
| "type": "boolean" | |
| } | |
| }, | |
| "customValidation": "requiresOneOf:issue_number,pull_number" | |
| }, | |
| "missing_data": { | |
| "defaultMax": 20, | |
| "fields": { | |
| "alternatives": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 256 | |
| }, | |
| "context": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 256 | |
| }, | |
| "data_type": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 128 | |
| }, | |
| "reason": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 256 | |
| } | |
| } | |
| }, | |
| "missing_tool": { | |
| "defaultMax": 20, | |
| "fields": { | |
| "alternatives": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 512 | |
| }, | |
| "reason": { | |
| "required": true, | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 256 | |
| }, | |
| "tool": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 128 | |
| } | |
| } | |
| }, | |
| "noop": { | |
| "defaultMax": 1, | |
| "fields": { | |
| "message": { | |
| "required": true, | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 65000 | |
| } | |
| } | |
| }, | |
| "report_incomplete": { | |
| "defaultMax": 5, | |
| "fields": { | |
| "details": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 65000 | |
| }, | |
| "reason": { | |
| "required": true, | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 1024 | |
| } | |
| } | |
| } | |
| } | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_safe_outputs_tools.cjs'); | |
| await main(); | |
| - name: Start MCP Gateway | |
| id: start-mcp-gateway | |
| env: | |
| GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST: ${{ vars.GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST || 'true' }} | |
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | |
| GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_CONFIG_PATH }} | |
| GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_TOOLS_PATH }} | |
| GH_AW_SINK_VISIBILITY: ${{ steps.determine-automatic-lockdown.outputs.visibility }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -eo pipefail | |
| mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config" | |
| if [ -n "${GITHUB_EVENT_PATH:-}" ] && [ -r "${GITHUB_EVENT_PATH}" ]; then | |
| GH_AW_SAFEOUTPUTS_EVENT_PATH="${RUNNER_TEMP}/gh-aw/safeoutputs/github_event.json" | |
| cp "${GITHUB_EVENT_PATH}" "${GH_AW_SAFEOUTPUTS_EVENT_PATH}" | |
| export GITHUB_EVENT_PATH="${GH_AW_SAFEOUTPUTS_EVENT_PATH}" | |
| fi | |
| # Export gateway environment variables for MCP config and gateway script | |
| export MCP_GATEWAY_PORT="8080" | |
| export MCP_GATEWAY_DOMAIN="awmg-mcpg" | |
| export MCP_GATEWAY_HOST_DOMAIN="localhost" | |
| MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') | |
| echo "::add-mask::${MCP_GATEWAY_API_KEY}" | |
| export MCP_GATEWAY_API_KEY | |
| export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" | |
| mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" | |
| export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" | |
| export MCP_GATEWAY_ALLOWED_MOUNT_ROOTS="${GITHUB_WORKSPACE}:rw,${RUNNER_TEMP}/gh-aw:ro,${RUNNER_TEMP}/gh-aw/safeoutputs:rw,/opt:ro,/tmp:rw,/usr/bin/gh:ro" | |
| export DEBUG="*" | |
| export GH_AW_ENGINE="pi" | |
| export GH_AW_MCP_CLI_SERVERS='["safeoutputs"]' | |
| MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0') | |
| MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0') | |
| source "${RUNNER_TEMP}/gh-aw/actions/resolve_docker_socket_gid.sh" | |
| export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network bridge -p 127.0.0.1:'"${MCP_GATEWAY_PORT}"':'"${MCP_GATEWAY_PORT}"' --name awmg-mcpg --add-host host.docker.internal:host-gateway --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GH_AW_SINK_VISIBILITY -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -e MCP_GATEWAY_ALLOWED_MOUNT_ROOTS -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -e OTEL_EXPORTER_OTLP_HEADERS -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.4.9' | |
| GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) | |
| cat << GH_AW_MCP_CONFIG_6648b861806769a0_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" | |
| { | |
| "mcpServers": { | |
| "safeoutputs": { | |
| "container": "ghcr.io/github/gh-aw-node", | |
| "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], | |
| "args": ["-w", "\${GITHUB_WORKSPACE}"], | |
| "entrypoint": "sh", | |
| "entrypointArgs": ["-c", "sh ${RUNNER_TEMP}/gh-aw/safeoutputs/start_safe_outputs_mcp.sh"], | |
| "env": { | |
| "DEBUG": "*", | |
| "DEFAULT_BRANCH": "\${DEFAULT_BRANCH}", | |
| "GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}", | |
| "GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}", | |
| "GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}", | |
| "GH_AW_MCP_LOG_DIR": "\${GH_AW_MCP_LOG_DIR}", | |
| "GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}", | |
| "GH_AW_SAFE_OUTPUTS_CONFIG_PATH": "\${GH_AW_SAFE_OUTPUTS_CONFIG_PATH}", | |
| "GH_AW_SAFE_OUTPUTS_TOOLS_PATH": "\${GH_AW_SAFE_OUTPUTS_TOOLS_PATH}", | |
| "GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST": "\${GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST}", | |
| "GITHUB_EVENT_NAME": "\${GITHUB_EVENT_NAME}", | |
| "GITHUB_EVENT_PATH": "\${GITHUB_EVENT_PATH}", | |
| "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}", | |
| "GITHUB_SHA": "\${GITHUB_SHA}", | |
| "GITHUB_TOKEN": "\${GITHUB_TOKEN}", | |
| "GITHUB_WORKSPACE": "\${GITHUB_WORKSPACE}", | |
| "RUNNER_TEMP": "\${RUNNER_TEMP}" | |
| }, | |
| "guard-policies": { | |
| "write-sink": { | |
| "accept": [ | |
| "*" | |
| ], | |
| "sink-visibility": "${GH_AW_SINK_VISIBILITY}" | |
| } | |
| } | |
| } | |
| }, | |
| "gateway": { | |
| "port": $MCP_GATEWAY_PORT, | |
| "domain": "${MCP_GATEWAY_DOMAIN}", | |
| "apiKey": "${MCP_GATEWAY_API_KEY}", | |
| "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}", | |
| "startupTimeout": 120, | |
| "opentelemetry": { | |
| "endpoint": "${OTEL_EXPORTER_OTLP_ENDPOINT}", | |
| "traceId": "${GITHUB_AW_OTEL_TRACE_ID}", | |
| "spanId": "${GITHUB_AW_OTEL_PARENT_SPAN_ID}" | |
| } | |
| } | |
| } | |
| GH_AW_MCP_CONFIG_6648b861806769a0_EOF | |
| - name: Mount MCP servers as CLIs | |
| id: mount-mcp-clis | |
| continue-on-error: true | |
| env: | |
| MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} | |
| MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }} | |
| MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs'); | |
| await main(); | |
| - name: Clean credentials | |
| continue-on-error: true | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh" | |
| - name: Audit pre-agent workspace | |
| id: pre_agent_audit | |
| continue-on-error: true | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh" | |
| - name: Start CLI Proxy | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GITHUB_API_URL: ${{ github.api_url }} | |
| GH_HOST: ${{ env.GH_HOST }} | |
| GITHUB_HOST: ${{ env.GITHUB_HOST }} | |
| GITHUB_ENTERPRISE_HOST: ${{ env.GITHUB_ENTERPRISE_HOST }} | |
| GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} | |
| GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} | |
| GH_AW_NETWORK_ISOLATION: 'true' | |
| CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' | |
| CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.9' | |
| run: | | |
| bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" | |
| - name: Execute Pi CLI | |
| id: agentic_execution | |
| timeout-minutes: 30 | |
| run: | | |
| set -o pipefail | |
| trap 'gh_aw_exit_code=$?; mkdir -p /tmp/gh-aw >/dev/null 2>&1 || true; printf "%s" "$gh_aw_exit_code" > /tmp/gh-aw/agent_execution_exit_code.txt || true; if [ "$gh_aw_exit_code" -ne 0 ]; then echo "::error::Agent execution exited with code $gh_aw_exit_code"; fi' EXIT | |
| printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt | |
| touch /tmp/gh-aw/agent-step-summary.md | |
| GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) | |
| export GH_AW_NODE_BIN | |
| (umask 177 && touch /tmp/gh-aw/agent-stdio.log) | |
| GH_AW_MAX_AI_CREDITS="${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}" | |
| printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.28.1/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.grafana.net\",\"*.sentry.io\",\"api.githubcopilot.com\",\"api.snapcraft.io\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"crl.geotrust.com\",\"crl.globalsign.com\",\"crl.identrust.com\",\"crl.sectigo.com\",\"crl.thawte.com\",\"crl.usertrust.com\",\"crl.verisign.com\",\"crl3.digicert.com\",\"crl4.digicert.com\",\"crls.ssl.com\",\"github.com\",\"host.docker.internal\",\"json-schema.org\",\"json.schemastore.org\",\"keyserver.ubuntu.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"ppa.launchpad.net\",\"raw.githubusercontent.com\",\"registry.npmjs.org\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\"],\"isolation\":true,\"topologyAttach\":[\"awmg-mcpg\",\"awmg-cli-proxy\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.4\",\"gpt-5.5\",\"gpt-5.6\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"auto\":[\"copilot/auto\",\"large\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\",\"kimi\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"detection\":[\"small\"],\"evals\":[\"small\"],\"fable\":[\"copilot/*fable*\",\"anthropic/*fable*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-3.6-flash\":[\"copilot/gemini-3.6*flash*\",\"google/gemini-3.6*flash*\",\"gemini/gemini-3.6*flash*\"],\"gemini-3.7-flash\":[\"copilot/gemini-3.7*flash*\",\"google/gemini-3.7*flash*\",\"gemini/gemini-3.7*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-omni\":[\"copilot/gemini-omni*\",\"google/gemini-omni*\",\"gemini/gemini-omni*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"gpt-5.6\":[\"copilot/gpt-5.6*\",\"openai/gpt-5.6*\"],\"grok\":[\"copilot/*grok*\",\"openai/*grok*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"kimi\":[\"copilot/kimi*\",\"openai/kimi*\"],\"kiwi\":[\"copilot/kiwi*\",\"openai/kiwi*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"lyria\":[\"google/lyria*\",\"gemini/lyria*\",\"copilot/lyria*\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mai-code-1-flash-picker\":[\"copilot/MAI-Code-1-Flash-picker*\",\"copilot/mai-code-1-flash-picker*\",\"openai/MAI-Code-1-Flash-picker*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"raptor-mini\":[\"copilot/raptor*\",\"openai/raptor*\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-5*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\",\"anthropic/*sonnet-5*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"veo\":[\"google/veo*\",\"gemini/veo*\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.28.1,squid=sha256:9d428af47899bf18ef2d5618075777d76ef344c91e76c1f44ec1aaa0ee347e5f,agent=sha256:5e3f6ee27eeae07195838b97ac4aa2f8aea42a7c55f1c0d3e17d8e88e294ad0d,api-proxy=sha256:288e7d2a12d5b430500d739f9c16e20bb1ed51b91f986f3f3eccde189f489f5c,cli-proxy=sha256:f931e5e1e13f765605d03ef9511fc755d779a51b76581ea14586e9871506a610\"},\"logging\":{\"proxyLogsDir\":\"/tmp/gh-aw/sandbox/firewall/logs\",\"auditDir\":\"/tmp/gh-aw/sandbox/firewall/audit\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" | |
| cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json | |
| export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" | |
| GH_AW_DOCKER_HOST="" | |
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | |
| GH_AW_DOCKER_HOST="${DOCKER_HOST}" | |
| fi | |
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | |
| GH_AW_CHROOT_BINARIES_SOURCE_PATH="${RUNNER_TEMP}/gh-aw" GH_AW_CHROOT_IDENTITY_HOME="${RUNNER_TEMP}/gh-aw/home" node "${RUNNER_TEMP}/gh-aw/actions/patch_awf_chroot_config.cjs" | |
| fi | |
| GH_AW_TOOL_CACHE_MOUNT="" | |
| GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}" | |
| if [ -d "$GH_AW_TOOL_CACHE" ]; then | |
| if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then | |
| GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro" | |
| fi | |
| fi | |
| # shellcheck disable=SC1003,SC2016,SC2086 | |
| awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} --env-all --exclude-env ACTIONS_ID_TOKEN_REQUEST_TOKEN --exclude-env ACTIONS_ID_TOKEN_REQUEST_URL --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --mount /tmp/gh-aw:/tmp/gh-aw:rw --log-level info --skip-pull --difc-proxy-host awmg-cli-proxy:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \ | |
| -- /bin/bash -c 'set +o histexpand; GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/shell_harness.cjs pi '\''export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\'\'''\''\n'\''\'\'''\'' '\''\'\'''\'':'\''\'\'''\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true; [ -n "$ERLANG_HOME" ] && export PATH="$ERLANG_HOME/bin:$PATH" || true && cd "${GITHUB_WORKSPACE}" && mkdir -p /tmp/gh-aw/pi-agent-dir && printf '\''\'\'''\''%s\n'\''\'\'''\'' '\''\'\'''\''{"providers":{"aw-gateway":{"api":"openai-completions","apiKey":"COPILOT_GITHUB_TOKEN","baseUrl":"http://api-proxy:10002","models":[{"id":"gpt-5.4"}]}}}'\''\'\'''\'' > /tmp/gh-aw/pi-agent-dir/models.json && cat /tmp/gh-aw/aw-prompts/prompt.txt | pi --print --mode json --no-session --model aw-gateway/gpt-5.4 --extension "${RUNNER_TEMP}/gh-aw/actions/pi_provider.cjs" --extension "${RUNNER_TEMP}/gh-aw/actions/pi_steering_extension.cjs" 2>&1 | tee /tmp/gh-aw/pi-streaming.jsonl'\''' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log | |
| env: | |
| AWF_REFLECT_ENABLED: 1 | |
| COPILOT_GITHUB_TOKEN: ${{ github.token }} | |
| GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} | |
| GH_AW_PHASE: agent | |
| GH_AW_PI_MODEL: copilot/gpt-5.4 | |
| GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt | |
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | |
| GH_AW_TIMEOUT_MINUTES: 30 | |
| GH_AW_VERSION: dev | |
| GH_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || github.token }} | |
| GITHUB_AW: true | |
| GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md | |
| GITHUB_WORKSPACE: ${{ github.workspace }} | |
| GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com | |
| GIT_AUTHOR_NAME: github-actions[bot] | |
| GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com | |
| GIT_COMMITTER_NAME: github-actions[bot] | |
| PI_CODING_AGENT_DIR: /tmp/gh-aw/pi-agent-dir | |
| PI_OFFLINE: 1 | |
| RUNNER_TEMP: ${{ runner.temp }} | |
| TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} | |
| - name: Stop CLI Proxy | |
| if: always() | |
| continue-on-error: true | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/stop_cli_proxy.sh" | |
| - name: Configure Git credentials | |
| env: | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" | |
| - name: Stop MCP Gateway | |
| if: always() | |
| continue-on-error: true | |
| env: | |
| MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} | |
| MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} | |
| GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} | |
| run: | | |
| bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID" | |
| - name: Redact secrets in logs | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs'); | |
| await main(); | |
| env: | |
| GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' | |
| SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} | |
| SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} | |
| SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Append agent step summary | |
| if: always() | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh" | |
| - name: Copy Safe Outputs | |
| if: always() | |
| env: | |
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | |
| run: | | |
| mkdir -p /tmp/gh-aw | |
| cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true | |
| - name: Ingest agent output | |
| id: collect_output | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | |
| GH_AW_ALLOWED_DOMAINS: "*.grafana.net,*.sentry.io,api.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GITHUB_API_URL: ${{ github.api_url }} | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs'); | |
| await main(); | |
| - name: Parse agent logs for step summary | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: /tmp/gh-aw/pi-streaming.jsonl | |
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_pi_log.cjs'); | |
| await main(); | |
| - name: Parse MCP Gateway logs for step summary | |
| if: always() | |
| id: parse-mcp-gateway | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs'); | |
| await main(); | |
| - name: Print firewall logs | |
| if: always() | |
| continue-on-error: true | |
| env: | |
| AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/print_firewall_logs.sh" --rootless | |
| - name: Parse token usage for step summary | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs'); | |
| await main(); | |
| - name: Print AWF reflect summary | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/awf_reflect_summary.cjs'); | |
| await main(); | |
| - name: Generate observability summary | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_observability_summary.cjs'); | |
| await main(core); | |
| - name: Write agent output placeholder if missing | |
| if: always() | |
| run: | | |
| if [ ! -f /tmp/gh-aw/agent_output.json ]; then | |
| echo '{"items":[]}' > /tmp/gh-aw/agent_output.json | |
| fi | |
| # Small dedicated copy of the agent output so safe-output processing | |
| # survives a failed or timed-out upload of the larger agent artifact | |
| - name: Upload agent output fallback artifact | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: agent-output-fallback | |
| path: | | |
| /tmp/gh-aw/agent_output.json | |
| /tmp/gh-aw/safeoutputs.jsonl | |
| if-no-files-found: ignore | |
| - name: Upload agent artifacts | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: agent | |
| path: | | |
| /tmp/gh-aw/aw-prompts/prompt.txt | |
| /tmp/gh-aw/pi-streaming.jsonl | |
| /tmp/gh-aw/redacted-urls.log | |
| /tmp/gh-aw/mcp-logs/ | |
| /tmp/gh-aw/proxy-logs/ | |
| !/tmp/gh-aw/proxy-logs/proxy-tls/ | |
| /tmp/gh-aw/agent_usage.json | |
| /tmp/gh-aw/agent-stdio.log | |
| /tmp/gh-aw/pre-agent-audit.txt | |
| /tmp/gh-aw/agent/ | |
| /tmp/gh-aw/github_rate_limits.jsonl | |
| /tmp/gh-aw/otel.jsonl | |
| /tmp/gh-aw/otlp-export-errors.jsonl | |
| /tmp/gh-aw/safeoutputs.jsonl | |
| /tmp/gh-aw/agent_output.json | |
| /tmp/gh-aw/aw-*.patch | |
| /tmp/gh-aw/aw-*.bundle | |
| /tmp/gh-aw/awf-config.json | |
| /tmp/gh-aw/sandbox/firewall/logs/ | |
| /tmp/gh-aw/sandbox/firewall/audit/ | |
| /tmp/gh-aw/sandbox/firewall/awf-reflect.json | |
| if-no-files-found: ignore | |
| conclusion: | |
| needs: | |
| - activation | |
| - agent | |
| - detection | |
| - evals | |
| - push_evals_state | |
| - safe_outputs | |
| if: > | |
| always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' || | |
| needs.activation.outputs.oauth_token_check_failed == 'true' || needs.activation.outputs.stale_lock_file_failed == 'true' || | |
| needs.activation.outputs.daily_ai_credits_exceeded == 'true') | |
| runs-on: ubuntu-slim | |
| permissions: | |
| actions: read | |
| issues: write | |
| pull-requests: write | |
| concurrency: | |
| group: "gh-aw-conclusion-issue-monster" | |
| cancel-in-progress: false | |
| queue: max | |
| env: | |
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | |
| outputs: | |
| incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }} | |
| noop_message: ${{ steps.noop.outputs.noop_message }} | |
| tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} | |
| total_count: ${{ steps.missing_tool.outputs.total_count }} | |
| steps: | |
| - name: Checkout actions folder | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| repository: github/gh-aw | |
| sparse-checkout: | | |
| actions | |
| clean: false | |
| persist-credentials: false | |
| - name: Setup Scripts | |
| id: setup | |
| uses: ./actions/setup | |
| with: | |
| destination: ${{ runner.temp }}/gh-aw/actions | |
| job-name: ${{ github.job }} | |
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | |
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | |
| env: | |
| GH_AW_SETUP_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-monster.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "0.84.2" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.1" | |
| GH_AW_INFO_ENGINE_ID: "pi" | |
| - name: Download agent output artifact | |
| id: download-agent-output | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| pattern: "{agent,agent-output-fallback}" | |
| merge-multiple: true | |
| path: /tmp/gh-aw/ | |
| - name: Setup agent output environment variable | |
| id: setup-agent-output-env | |
| if: steps.download-agent-output.outcome == 'success' | |
| run: | | |
| mkdir -p /tmp/gh-aw/ | |
| find "/tmp/gh-aw/" -type f -print | |
| if [ -f "/tmp/gh-aw/agent_output.json" ]; then | |
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Download Safe Outputs Items Manifest | |
| id: download-safe-outputs-manifest | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: safe-outputs-items | |
| path: /tmp/gh-aw/ | |
| - name: Download evals artifact | |
| id: download-evals-artifact | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: evals | |
| path: /tmp/gh-aw/evals/ | |
| - name: Collect usage artifact files | |
| if: always() | |
| continue-on-error: true | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/collect_usage_artifact_files.sh" | |
| - name: Upload usage artifact | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: usage | |
| path: | | |
| /tmp/gh-aw/usage/aw_info.json | |
| /tmp/gh-aw/usage/aw-info.jsonl | |
| /tmp/gh-aw/usage/agent_usage.json | |
| /tmp/gh-aw/usage/agent_usage.jsonl | |
| /tmp/gh-aw/usage/detection_usage.jsonl | |
| /tmp/gh-aw/usage/evals.jsonl | |
| /tmp/gh-aw/usage/github_rate_limits.jsonl | |
| /tmp/gh-aw/usage/agent/token_usage.jsonl | |
| /tmp/gh-aw/usage/detection/token_usage.jsonl | |
| /tmp/gh-aw/usage/activity/summary.json | |
| if-no-files-found: ignore | |
| - name: Restore daily AIC usage cache | |
| id: restore-daily-aic-cache-conclusion | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| key: agentic-workflow-usage-issuemonster-${{ github.run_id }} | |
| restore-keys: agentic-workflow-usage-issuemonster- | |
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | |
| - name: Write daily AIC usage cache entry | |
| id: write-daily-aic-cache | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| github-token: ${{ github.token }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/write_daily_aic_usage_cache.cjs'); | |
| await main(); | |
| - name: Save daily AIC usage cache | |
| id: save-daily-aic-cache | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| key: agentic-workflow-usage-issuemonster-${{ github.run_id }} | |
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | |
| - name: Upload daily AIC usage cache artifact | |
| id: upload-daily-aic-cache | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: aic-usage-cache | |
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | |
| if-no-files-found: ignore | |
| retention-days: 7 | |
| - name: Process no-op messages | |
| id: noop | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | |
| GH_AW_NOOP_MAX: "1" | |
| GH_AW_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-monster.md" | |
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} | |
| GH_AW_NOOP_REPORT_AS_ISSUE: "false" | |
| GH_AW_AIC: ${{ needs.agent.outputs.aic }} | |
| GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} | |
| GH_AW_EVALS_AIC: ${{ needs.evals.outputs.aic }} | |
| GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }} | |
| GH_AW_WORKFLOW_ID: "issue-monster" | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs'); | |
| await main(); | |
| - name: Log detection run | |
| id: detection_runs | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | |
| GH_AW_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-monster.md" | |
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} | |
| GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_detection_runs.cjs'); | |
| await main(); | |
| - name: Record missing tool | |
| id: missing_tool | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | |
| GH_AW_MISSING_TOOL_CREATE_ISSUE: "true" | |
| GH_AW_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-monster.md" | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs'); | |
| await main(); | |
| - name: Record incomplete | |
| id: report_incomplete | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | |
| GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true" | |
| GH_AW_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-monster.md" | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs'); | |
| await main(); | |
| - name: Handle agent failure | |
| id: handle_agent_failure | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | |
| GH_AW_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-monster.md" | |
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} | |
| GH_AW_WORKFLOW_ID: "issue-monster" | |
| GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "12" | |
| GH_AW_ENGINE_ID: "pi" | |
| GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} | |
| GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }} | |
| GH_AW_AI_CREDITS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.ai_credits_rate_limit_error || 'false' }} | |
| GH_AW_UNKNOWN_MODEL_AI_CREDITS: ${{ needs.agent.outputs.unknown_model_ai_credits || 'false' }} | |
| GH_AW_AIC: ${{ needs.agent.outputs.aic }} | |
| GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} | |
| GH_AW_EVALS_AIC: ${{ needs.evals.outputs.aic }} | |
| GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} | |
| GH_AW_ASSIGNMENT_ERRORS: ${{ needs.safe_outputs.outputs.assign_to_agent_assignment_errors }} | |
| GH_AW_ASSIGNMENT_ERROR_COUNT: ${{ needs.safe_outputs.outputs.assign_to_agent_assignment_error_count }} | |
| GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} | |
| GH_AW_OAUTH_TOKEN_CHECK_FAILED: ${{ needs.activation.outputs.oauth_token_check_failed }} | |
| GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} | |
| GH_AW_DAILY_AI_CREDITS_EXCEEDED: ${{ needs.activation.outputs.daily_ai_credits_exceeded }} | |
| GH_AW_DAILY_AI_CREDITS_TOTAL_EFFECTIVE_TOKENS: ${{ needs.activation.outputs.daily_ai_credits_total_effective_tokens }} | |
| GH_AW_DAILY_AI_CREDITS_THRESHOLD: ${{ needs.activation.outputs.daily_ai_credits_threshold }} | |
| GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🍪 *Om nom nom by [{workflow_name}]({run_url})*{ai_credits_suffix}{history_link}\",\"runStarted\":\"🍪 ISSUE! ISSUE! [{workflow_name}]({run_url}) hungry for issues on this {event_type}! Om nom nom...\",\"runSuccess\":\"🍪 YUMMY! [{workflow_name}]({run_url}) ate the issues! That was DELICIOUS! Me want MORE! 😋\",\"runFailure\":\"🍪 Aww... [{workflow_name}]({run_url}) {status}. No cookie for monster today... 😢\"}" | |
| GH_AW_GROUP_REPORTS: "false" | |
| GH_AW_FAILURE_REPORT_AS_ISSUE: "true" | |
| GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true" | |
| GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true" | |
| GH_AW_TIMEOUT_MINUTES: "30" | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); | |
| await main(); | |
| - name: Report failed jobs | |
| id: report_failed_jobs | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | |
| GH_AW_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-monster.md" | |
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| GH_AW_REPORT_FAILED_JOBS: "true" | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/report_failed_jobs.cjs'); | |
| await main(); | |
| detection: | |
| needs: | |
| - activation | |
| - agent | |
| if: always() && needs.agent.result != 'skipped' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| copilot-requests: write | |
| env: | |
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | |
| outputs: | |
| aic: ${{ steps.parse_detection_token_usage.outputs.aic }} | |
| detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} | |
| detection_reason: ${{ steps.detection_conclusion.outputs.reason }} | |
| detection_success: ${{ steps.detection_conclusion.outputs.success }} | |
| steps: | |
| - name: Checkout actions folder | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| repository: github/gh-aw | |
| sparse-checkout: | | |
| actions | |
| clean: false | |
| persist-credentials: false | |
| - name: Setup Scripts | |
| id: setup | |
| uses: ./actions/setup | |
| with: | |
| destination: ${{ runner.temp }}/gh-aw/actions | |
| job-name: ${{ github.job }} | |
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | |
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | |
| env: | |
| GH_AW_SETUP_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-monster.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "0.84.2" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.1" | |
| GH_AW_INFO_ENGINE_ID: "pi" | |
| - name: Download activation artifact | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: activation | |
| path: /tmp/gh-aw | |
| - name: Download agent output artifact | |
| id: download-agent-output | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| pattern: "{agent,agent-output-fallback}" | |
| merge-multiple: true | |
| path: /tmp/gh-aw/ | |
| - name: Setup agent output environment variable | |
| id: setup-agent-output-env | |
| if: steps.download-agent-output.outcome == 'success' | |
| run: | | |
| mkdir -p /tmp/gh-aw/ | |
| find "/tmp/gh-aw/" -type f -print | |
| if [ -f "/tmp/gh-aw/agent_output.json" ]; then | |
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Checkout repository for patch context | |
| if: needs.agent.outputs.has_patch == 'true' | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| # --- Threat Detection --- | |
| - name: Clean stale firewall files from agent artifact | |
| run: | | |
| rm -rf /tmp/gh-aw/sandbox/firewall/logs | |
| rm -rf /tmp/gh-aw/sandbox/firewall/audit | |
| - name: Download container images | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.28.1@sha256:5e3f6ee27eeae07195838b97ac4aa2f8aea42a7c55f1c0d3e17d8e88e294ad0d ghcr.io/github/gh-aw-firewall/api-proxy:0.28.1@sha256:288e7d2a12d5b430500d739f9c16e20bb1ed51b91f986f3f3eccde189f489f5c ghcr.io/github/gh-aw-firewall/squid:0.28.1@sha256:9d428af47899bf18ef2d5618075777d76ef344c91e76c1f44ec1aaa0ee347e5f | |
| - name: Check if detection needed | |
| id: detection_guard | |
| if: always() | |
| env: | |
| OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }} | |
| HAS_PATCH: ${{ needs.agent.outputs.has_patch }} | |
| run: | | |
| if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then | |
| echo "run_detection=true" >> "$GITHUB_OUTPUT" | |
| echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" | |
| else | |
| echo "run_detection=false" >> "$GITHUB_OUTPUT" | |
| echo "Detection skipped: no agent outputs or patches to analyze" | |
| fi | |
| - name: Clear MCP Config for detection | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| run: | | |
| rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json" | |
| rm -f "$HOME/.copilot/mcp-config.json" | |
| rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" | |
| - name: Prepare threat detection files | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| run: | | |
| bash "${RUNNER_TEMP}/gh-aw/actions/prepare_threat_detection_files.sh" | |
| - name: Setup threat detection | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| WORKFLOW_NAME: "Issue Monster" | |
| WORKFLOW_DESCRIPTION: "The Cookie Monster of issues - assigns issues to Copilot coding agent one at a time" | |
| HAS_PATCH: ${{ needs.agent.outputs.has_patch }} | |
| GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" | |
| GH_AW_DETECTION_SKIP_PROMPT_SUMMARY: "true" | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_threat_detection.cjs'); | |
| await main(); | |
| - name: Ensure threat-detection directory and log | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| run: | | |
| mkdir -p /tmp/gh-aw/threat-detection | |
| touch /tmp/gh-aw/threat-detection/detection.log | |
| - name: Install AWF binary | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.28.1 | |
| - name: Install GitHub Copilot CLI | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" | |
| env: | |
| GH_HOST: github.com | |
| GH_AW_COMPILED_VERSION: dev | |
| - name: Install threat-detect binary | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| continue-on-error: true | |
| run: | | |
| bash "${RUNNER_TEMP}/gh-aw/actions/install_threat_detect_binary.sh" latest | |
| - name: Execute threat detection with AWF | |
| id: detection_agentic_execution | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| continue-on-error: true | |
| env: | |
| AWF_REFLECT_ENABLED: 1 | |
| COPILOT_AGENT_RUNNER_TYPE: STANDALONE | |
| COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode | |
| COPILOT_GITHUB_TOKEN: ${{ github.token }} | |
| COPILOT_MODEL: gpt-5.4 | |
| GH_AW_LLM_PROVIDER: github | |
| GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }} | |
| GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} | |
| GH_AW_PHASE: detection | |
| GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt | |
| GH_AW_TIMEOUT_MINUTES: 20 | |
| GH_AW_VERSION: dev | |
| GITHUB_API_URL: ${{ github.api_url }} | |
| GITHUB_AW: true | |
| GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows | |
| GITHUB_HEAD_REF: ${{ github.head_ref }} | |
| GITHUB_REF_NAME: ${{ github.ref_name }} | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md | |
| GITHUB_WORKSPACE: ${{ github.workspace }} | |
| GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com | |
| GIT_AUTHOR_NAME: github-actions[bot] | |
| GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com | |
| GIT_COMMITTER_NAME: github-actions[bot] | |
| RUNNER_TEMP: ${{ runner.temp }} | |
| S2STOKENS: true | |
| TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} | |
| WORKFLOW_NAME: "Issue Monster" | |
| WORKFLOW_DESCRIPTION: "The Cookie Monster of issues - assigns issues to Copilot coding agent one at a time" | |
| HAS_PATCH: ${{ needs.agent.outputs.has_patch }} | |
| GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" | |
| run: | | |
| set -o pipefail | |
| printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt | |
| GH_AW_COPILOT_SRC="$(command -v copilot 2>/dev/null || true)" | |
| if [ -z "$GH_AW_COPILOT_SRC" ] || [ ! -x "$GH_AW_COPILOT_SRC" ]; then | |
| echo "GitHub Copilot CLI executable not found on PATH after installation" >&2 | |
| exit 127 | |
| fi | |
| GH_AW_COPILOT_BIN="${RUNNER_TEMP}/gh-aw/bin/copilot" | |
| mkdir -p "${RUNNER_TEMP}/gh-aw/bin" | |
| if [ "$GH_AW_COPILOT_SRC" != "$GH_AW_COPILOT_BIN" ]; then | |
| cp "$GH_AW_COPILOT_SRC" "$GH_AW_COPILOT_BIN" | |
| fi | |
| chmod 755 "$GH_AW_COPILOT_BIN" | |
| (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) | |
| GH_AW_MAX_AI_CREDITS="${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }}" | |
| printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.28.1/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"github.com\",\"host.docker.internal\",\"raw.githubusercontent.com\",\"registry.npmjs.org\",\"telemetry.enterprise.githubcopilot.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.4\",\"gpt-5.5\",\"gpt-5.6\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"auto\":[\"copilot/auto\",\"large\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\",\"kimi\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"detection\":[\"small\"],\"evals\":[\"small\"],\"fable\":[\"copilot/*fable*\",\"anthropic/*fable*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-3.6-flash\":[\"copilot/gemini-3.6*flash*\",\"google/gemini-3.6*flash*\",\"gemini/gemini-3.6*flash*\"],\"gemini-3.7-flash\":[\"copilot/gemini-3.7*flash*\",\"google/gemini-3.7*flash*\",\"gemini/gemini-3.7*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-omni\":[\"copilot/gemini-omni*\",\"google/gemini-omni*\",\"gemini/gemini-omni*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"gpt-5.6\":[\"copilot/gpt-5.6*\",\"openai/gpt-5.6*\"],\"grok\":[\"copilot/*grok*\",\"openai/*grok*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"kimi\":[\"copilot/kimi*\",\"openai/kimi*\"],\"kiwi\":[\"copilot/kiwi*\",\"openai/kiwi*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"lyria\":[\"google/lyria*\",\"gemini/lyria*\",\"copilot/lyria*\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mai-code-1-flash-picker\":[\"copilot/MAI-Code-1-Flash-picker*\",\"copilot/mai-code-1-flash-picker*\",\"openai/MAI-Code-1-Flash-picker*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"raptor-mini\":[\"copilot/raptor*\",\"openai/raptor*\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-5*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\",\"anthropic/*sonnet-5*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"veo\":[\"google/veo*\",\"gemini/veo*\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.28.1,squid=sha256:9d428af47899bf18ef2d5618075777d76ef344c91e76c1f44ec1aaa0ee347e5f,agent=sha256:5e3f6ee27eeae07195838b97ac4aa2f8aea42a7c55f1c0d3e17d8e88e294ad0d,api-proxy=sha256:288e7d2a12d5b430500d739f9c16e20bb1ed51b91f986f3f3eccde189f489f5c,cli-proxy=sha256:f931e5e1e13f765605d03ef9511fc755d779a51b76581ea14586e9871506a610\"},\"logging\":{\"proxyLogsDir\":\"/tmp/gh-aw/sandbox/firewall/logs\",\"auditDir\":\"/tmp/gh-aw/sandbox/firewall/audit\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" | |
| cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json | |
| export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" | |
| GH_AW_DOCKER_HOST="" | |
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | |
| GH_AW_DOCKER_HOST="${DOCKER_HOST}" | |
| fi | |
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | |
| _GH_AW_CHROOT_JSON=$(jq -c --arg src "${RUNNER_TEMP}/gh-aw" --arg user "$(id -un)" --argjson uid "$(id -u)" --argjson gid "$(id -g)" --arg home "${RUNNER_TEMP}/gh-aw/home" '.chroot={"binariesSourcePath":$src,"identity":{"user":$user,"uid":$uid,"gid":$gid,"home":$home}}' "${RUNNER_TEMP}/gh-aw/awf-config.json") || { echo "chroot config patch failed" >&2; exit 1; } | |
| printf '%s\n' "$_GH_AW_CHROOT_JSON" > "${RUNNER_TEMP}/gh-aw/awf-config.json" | |
| printf '%s\n' "$_GH_AW_CHROOT_JSON" > "${RUNNER_TEMP}/gh-aw/awf-config.json" | |
| fi | |
| GH_AW_TOOL_CACHE_MOUNT="" | |
| GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}" | |
| if [ -d "$GH_AW_TOOL_CACHE" ]; then | |
| if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then | |
| GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro" | |
| fi | |
| fi | |
| # shellcheck disable=SC1003,SC2016,SC2086 | |
| awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} --env-all --exclude-env ACTIONS_ID_TOKEN_REQUEST_TOKEN --exclude-env ACTIONS_ID_TOKEN_REQUEST_URL --exclude-env COPILOT_GITHUB_TOKEN --mount /tmp/gh-aw:/tmp/gh-aw:rw --mount /tmp/gh-aw/threat-detection:/tmp/gh-aw/threat-detection:rw --log-level info --skip-pull \ | |
| -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true; [ -n "$ERLANG_HOME" ] && export PATH="$ERLANG_HOME/bin:$PATH" || true && threat-detect --engine copilot --output /tmp/gh-aw/threat-detection/detection_result.json /tmp/gh-aw/threat-detection' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log | |
| - name: Render detection log | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/render_detection_log.cjs'); | |
| await main(); | |
| - name: Upload threat detection artifact | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: detection | |
| path: /tmp/gh-aw/threat-detection/detection_result.json | |
| if-no-files-found: ignore | |
| - name: Parse threat detection token usage for step summary | |
| id: parse_detection_token_usage | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_TOKEN_USAGE_SUMMARY_TITLE: Threat Detection Token Usage | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs'); | |
| await main(); | |
| - name: Conclude threat detection | |
| id: detection_conclusion | |
| if: always() | |
| continue-on-error: true | |
| env: | |
| RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} | |
| DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }} | |
| GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" | |
| run: | | |
| bash "${RUNNER_TEMP}/gh-aw/actions/conclude_threat_detection.sh" /tmp/gh-aw/threat-detection/detection_result.json | |
| evals: | |
| needs: | |
| - activation | |
| - agent | |
| - detection | |
| if: always() && needs.agent.result != 'skipped' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| copilot-requests: write | |
| env: | |
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | |
| outputs: | |
| aic: ${{ steps.parse-mcp-gateway.outputs.aic }} | |
| steps: | |
| - name: Checkout actions folder | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| repository: github/gh-aw | |
| sparse-checkout: | | |
| actions | |
| clean: false | |
| persist-credentials: false | |
| - name: Setup Scripts | |
| id: setup | |
| uses: ./actions/setup | |
| with: | |
| destination: ${{ runner.temp }}/gh-aw/actions | |
| job-name: ${{ github.job }} | |
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | |
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | |
| env: | |
| GH_AW_SETUP_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-monster.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "0.84.2" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.1" | |
| GH_AW_INFO_ENGINE_ID: "pi" | |
| - name: Download agent output artifact | |
| id: download-agent-output | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| pattern: "{agent,agent-output-fallback}" | |
| merge-multiple: true | |
| path: /tmp/gh-aw/ | |
| - name: Setup agent output environment variable | |
| id: setup-agent-output-env | |
| if: steps.download-agent-output.outcome == 'success' | |
| run: | | |
| mkdir -p /tmp/gh-aw/ | |
| find "/tmp/gh-aw/" -type f -print | |
| if [ -f "/tmp/gh-aw/agent_output.json" ]; then | |
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | |
| fi | |
| # --- BinEval Evaluations --- | |
| - name: Clean stale firewall files from agent artifact | |
| run: | | |
| rm -rf /tmp/gh-aw/sandbox/firewall/logs | |
| rm -rf /tmp/gh-aw/sandbox/firewall/audit | |
| - name: Download container images | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.28.1@sha256:5e3f6ee27eeae07195838b97ac4aa2f8aea42a7c55f1c0d3e17d8e88e294ad0d ghcr.io/github/gh-aw-firewall/api-proxy:0.28.1@sha256:288e7d2a12d5b430500d739f9c16e20bb1ed51b91f986f3f3eccde189f489f5c ghcr.io/github/gh-aw-firewall/squid:0.28.1@sha256:9d428af47899bf18ef2d5618075777d76ef344c91e76c1f44ec1aaa0ee347e5f | |
| - name: Prepare evals files | |
| run: | | |
| mkdir -p /tmp/gh-aw/evals | |
| cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/evals/agent_output.json 2>/dev/null || true | |
| cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/evals/prompt.txt 2>/dev/null || true | |
| ls -la /tmp/gh-aw/evals/ 2>/dev/null || true | |
| - name: Setup BinEval evaluations | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_EVALS_QUESTIONS: '[{"id":"issue_assigned","question":"Did the agent assign at least one issue to the Copilot coding agent, or correctly skip when no suitable issues were found?"},{"id":"single_issue_scoped","question":"Does the agent output show that at most one issue was assigned to Copilot per run?"}]' | |
| GH_AW_EVALS_MODEL: "copilot/gpt-5.4" | |
| GH_AW_EVALS_PHASE: setup | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/run_evals.cjs'); | |
| await main(); | |
| - name: Ensure evals directory and log | |
| run: | | |
| mkdir -p /tmp/gh-aw/evals | |
| touch /tmp/gh-aw/evals/evals.log | |
| - name: Setup Node.js | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: '24' | |
| package-manager-cache: false | |
| - name: Install AWF binary | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.28.1 --rootless | |
| - name: Install Pi CLI | |
| run: npm install --ignore-scripts -g @earendil-works/pi-coding-agent@0.84.2 | |
| - name: Execute Pi CLI | |
| if: always() | |
| continue-on-error: true | |
| id: evals_agentic_execution | |
| timeout-minutes: 20 | |
| run: | | |
| set -o pipefail | |
| trap 'gh_aw_exit_code=$?; mkdir -p /tmp/gh-aw >/dev/null 2>&1 || true; printf "%s" "$gh_aw_exit_code" > /tmp/gh-aw/agent_execution_exit_code.txt || true; if [ "$gh_aw_exit_code" -ne 0 ]; then echo "::error::Agent execution exited with code $gh_aw_exit_code"; fi' EXIT | |
| printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt | |
| touch /tmp/gh-aw/agent-step-summary.md | |
| GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) | |
| export GH_AW_NODE_BIN | |
| (umask 177 && touch /tmp/gh-aw/evals/evals.log) | |
| GH_AW_MAX_AI_CREDITS="${{ vars.GH_AW_DEFAULT_EVALS_MAX_AI_CREDITS || '400' }}" | |
| printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.28.1/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.githubcopilot.com\",\"github.com\",\"host.docker.internal\",\"raw.githubusercontent.com\",\"registry.npmjs.org\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.4\",\"gpt-5.5\",\"gpt-5.6\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"auto\":[\"copilot/auto\",\"large\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\",\"kimi\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"detection\":[\"small\"],\"evals\":[\"small\"],\"fable\":[\"copilot/*fable*\",\"anthropic/*fable*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-3.6-flash\":[\"copilot/gemini-3.6*flash*\",\"google/gemini-3.6*flash*\",\"gemini/gemini-3.6*flash*\"],\"gemini-3.7-flash\":[\"copilot/gemini-3.7*flash*\",\"google/gemini-3.7*flash*\",\"gemini/gemini-3.7*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-omni\":[\"copilot/gemini-omni*\",\"google/gemini-omni*\",\"gemini/gemini-omni*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"gpt-5.6\":[\"copilot/gpt-5.6*\",\"openai/gpt-5.6*\"],\"grok\":[\"copilot/*grok*\",\"openai/*grok*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"kimi\":[\"copilot/kimi*\",\"openai/kimi*\"],\"kiwi\":[\"copilot/kiwi*\",\"openai/kiwi*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"lyria\":[\"google/lyria*\",\"gemini/lyria*\",\"copilot/lyria*\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mai-code-1-flash-picker\":[\"copilot/MAI-Code-1-Flash-picker*\",\"copilot/mai-code-1-flash-picker*\",\"openai/MAI-Code-1-Flash-picker*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"raptor-mini\":[\"copilot/raptor*\",\"openai/raptor*\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-5*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\",\"anthropic/*sonnet-5*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"veo\":[\"google/veo*\",\"gemini/veo*\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.28.1,squid=sha256:9d428af47899bf18ef2d5618075777d76ef344c91e76c1f44ec1aaa0ee347e5f,agent=sha256:5e3f6ee27eeae07195838b97ac4aa2f8aea42a7c55f1c0d3e17d8e88e294ad0d,api-proxy=sha256:288e7d2a12d5b430500d739f9c16e20bb1ed51b91f986f3f3eccde189f489f5c,cli-proxy=sha256:f931e5e1e13f765605d03ef9511fc755d779a51b76581ea14586e9871506a610\"},\"logging\":{\"proxyLogsDir\":\"/tmp/gh-aw/sandbox/firewall/logs\",\"auditDir\":\"/tmp/gh-aw/sandbox/firewall/audit\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" | |
| cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json | |
| export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" | |
| GH_AW_DOCKER_HOST="" | |
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | |
| GH_AW_DOCKER_HOST="${DOCKER_HOST}" | |
| fi | |
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | |
| GH_AW_CHROOT_BINARIES_SOURCE_PATH="${RUNNER_TEMP}/gh-aw" GH_AW_CHROOT_IDENTITY_HOME="${RUNNER_TEMP}/gh-aw/home" node "${RUNNER_TEMP}/gh-aw/actions/patch_awf_chroot_config.cjs" | |
| fi | |
| GH_AW_TOOL_CACHE_MOUNT="" | |
| GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}" | |
| if [ -d "$GH_AW_TOOL_CACHE" ]; then | |
| if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then | |
| GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro" | |
| fi | |
| fi | |
| # shellcheck disable=SC1003,SC2016,SC2086 | |
| awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} --env-all --exclude-env ACTIONS_ID_TOKEN_REQUEST_TOKEN --exclude-env ACTIONS_ID_TOKEN_REQUEST_URL --mount /tmp/gh-aw:/tmp/gh-aw:rw --log-level info --skip-pull \ | |
| -- /bin/bash -c 'set +o histexpand; GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/shell_harness.cjs pi '\'': "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\'\'''\''\n'\''\'\'''\'' '\''\'\'''\'':'\''\'\'''\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true; [ -n "$ERLANG_HOME" ] && export PATH="$ERLANG_HOME/bin:$PATH" || true && cd "${GITHUB_WORKSPACE}" && mkdir -p /tmp/gh-aw/pi-agent-dir && printf '\''\'\'''\''%s\n'\''\'\'''\'' '\''\'\'''\''{"providers":{"aw-gateway":{"api":"openai-completions","apiKey":"COPILOT_GITHUB_TOKEN","baseUrl":"http://api-proxy:10002","models":[{"id":"gpt-5.4"}]}}}'\''\'\'''\'' > /tmp/gh-aw/pi-agent-dir/models.json && cat /tmp/gh-aw/aw-prompts/prompt.txt | pi --print --mode json --no-session --model aw-gateway/gpt-5.4 --extension "${RUNNER_TEMP}/gh-aw/actions/pi_provider.cjs" --extension "${RUNNER_TEMP}/gh-aw/actions/pi_steering_extension.cjs" 2>&1 | tee /tmp/gh-aw/pi-streaming.jsonl'\''' 2>&1 | tee -a /tmp/gh-aw/evals/evals.log | |
| env: | |
| AWF_REFLECT_ENABLED: 1 | |
| COPILOT_GITHUB_TOKEN: ${{ github.token }} | |
| GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} | |
| GH_AW_PHASE: evals | |
| GH_AW_PI_MODEL: copilot/gpt-5.4 | |
| GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt | |
| GH_AW_TIMEOUT_MINUTES: 20 | |
| GH_AW_VERSION: dev | |
| GITHUB_AW: true | |
| GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md | |
| GITHUB_WORKSPACE: ${{ github.workspace }} | |
| GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com | |
| GIT_AUTHOR_NAME: github-actions[bot] | |
| GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com | |
| GIT_COMMITTER_NAME: github-actions[bot] | |
| PI_CODING_AGENT_DIR: /tmp/gh-aw/pi-agent-dir | |
| PI_OFFLINE: 1 | |
| RUNNER_TEMP: ${{ runner.temp }} | |
| TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} | |
| - name: Parse MCP Gateway logs for step summary | |
| if: always() | |
| id: parse-mcp-gateway | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs'); | |
| await main(); | |
| - name: Parse BinEval results | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_EVALS_QUESTIONS: '[{"id":"issue_assigned","question":"Did the agent assign at least one issue to the Copilot coding agent, or correctly skip when no suitable issues were found?"},{"id":"single_issue_scoped","question":"Does the agent output show that at most one issue was assigned to Copilot per run?"}]' | |
| GH_AW_EVALS_MODEL: "copilot/gpt-5.4" | |
| GH_AW_EVALS_PHASE: parse | |
| GITHUB_RUN_ID: ${{ github.run_id }} | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/run_evals.cjs'); | |
| await main(); | |
| - name: Redact secrets in evals results | |
| id: redact_evals_results | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_evals_results.cjs'); | |
| await main(); | |
| - name: Render evals results to step summary | |
| if: steps.redact_evals_results.outcome == 'success' | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/render_evals_summary.cjs'); | |
| await main(); | |
| - name: Upload evals results | |
| if: steps.redact_evals_results.outcome == 'success' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: evals | |
| path: /tmp/gh-aw/evals.jsonl | |
| if-no-files-found: ignore | |
| - name: Restore actions folder | |
| if: always() | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| repository: github/gh-aw | |
| sparse-checkout: | | |
| actions/setup | |
| sparse-checkout-cone-mode: true | |
| clean: false | |
| persist-credentials: false | |
| pre_activation: | |
| runs-on: ubuntu-slim | |
| permissions: | |
| contents: read | |
| issues: read | |
| pull-requests: read | |
| env: | |
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | |
| outputs: | |
| activated: ${{ steps.check_membership.outputs.is_team_member == 'true' && steps.check_skip_if_match.outputs.skip_check_ok == 'true' && steps.check_skip_if_no_match.outputs.skip_no_match_check_ok == 'true' && steps.check_skip_if_check_failing.outputs.skip_if_check_failing_ok == 'true' }} | |
| has_issues: ${{ steps.search.outputs.has_issues }} | |
| issue_context: ${{ steps.search.outputs.issue_context }} | |
| issue_count: ${{ steps.search.outputs.issue_count }} | |
| issue_list: ${{ steps.search.outputs.issue_list }} | |
| issue_numbers: ${{ steps.search.outputs.issue_numbers }} | |
| matched_command: '' | |
| retry_blocked_list: ${{ steps.search.outputs.retry_blocked_list }} | |
| search_result: ${{ steps.search.outcome }} | |
| setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} | |
| setup-span-id: ${{ steps.setup.outputs.span-id }} | |
| setup-trace-id: ${{ steps.setup.outputs.trace-id }} | |
| steps: | |
| - name: Checkout actions folder | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| repository: github/gh-aw | |
| sparse-checkout: | | |
| actions | |
| clean: false | |
| persist-credentials: false | |
| - name: Setup Scripts | |
| id: setup | |
| uses: ./actions/setup | |
| with: | |
| destination: ${{ runner.temp }}/gh-aw/actions | |
| job-name: ${{ github.job }} | |
| env: | |
| GH_AW_SETUP_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-monster.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "0.84.2" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.1" | |
| GH_AW_INFO_ENGINE_ID: "pi" | |
| - name: Check team membership for workflow | |
| id: check_membership | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_REQUIRED_ROLES: "admin,maintainer,write" | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs'); | |
| await main(); | |
| - name: Check skip-if-match query | |
| id: check_skip_if_match | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_SKIP_QUERY: "is:pr is:open is:draft author:app/copilot-swe-agent" | |
| GH_AW_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_SKIP_MAX_MATCHES: "5" | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/check_skip_if_match.cjs'); | |
| await main(); | |
| - name: Check skip-if-no-match query | |
| id: check_skip_if_no_match | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_SKIP_QUERY: "is:issue is:open" | |
| GH_AW_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_SKIP_MIN_MATCHES: "1" | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/check_skip_if_no_match.cjs'); | |
| await main(); | |
| - name: Check skip-if-check-failing | |
| id: check_skip_if_check_failing | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_SKIP_CHECK_INCLUDE: "[\"build\",\"test\",\"lint-go\",\"lint-js\"]" | |
| GH_AW_SKIP_CHECK_ALLOW_PENDING: "true" | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/check_skip_if_check_failing.cjs'); | |
| await main(); | |
| - name: Search for candidate issues | |
| id: search | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { owner, repo } = context.repo; | |
| const MAX_ISSUES_WITH_BODY_CONTEXT = 8; | |
| const BODY_SNIPPET_MAX_LENGTH = 600; | |
| try { | |
| // Check for recent rate-limited PRs to avoid scheduling more work during rate limiting | |
| core.info('Checking for recent rate-limited PRs...'); | |
| const rateLimitCheckDate = new Date(); | |
| rateLimitCheckDate.setHours(rateLimitCheckDate.getHours() - 1); // Check last hour | |
| // Format as YYYY-MM-DDTHH:MM:SS for GitHub search API | |
| const rateLimitCheckISO = rateLimitCheckDate.toISOString().split('.')[0] + 'Z'; | |
| const recentPRsQuery = `is:pr author:app/copilot-swe-agent created:>${rateLimitCheckISO} repo:${owner}/${repo}`; | |
| const recentPRsResponse = await github.rest.search.issuesAndPullRequests({ | |
| q: recentPRsQuery, | |
| per_page: 10, | |
| sort: 'created', | |
| order: 'desc' | |
| }); | |
| core.info(`Found ${recentPRsResponse.data.total_count} recent Copilot PRs to check for rate limiting`); | |
| // Check if any recent PRs have rate limit indicators | |
| let rateLimitDetected = false; | |
| for (const pr of recentPRsResponse.data.items) { | |
| try { | |
| const prTimelineQuery = ` | |
| query($owner: String!, $repo: String!, $number: Int!) { | |
| repository(owner: $owner, name: $repo) { | |
| pullRequest(number: $number) { | |
| timelineItems(first: 50, itemTypes: [ISSUE_COMMENT]) { | |
| nodes { | |
| __typename | |
| ... on IssueComment { | |
| body | |
| createdAt | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| `; | |
| const prTimelineResult = await github.graphql(prTimelineQuery, { | |
| owner, | |
| repo, | |
| number: pr.number | |
| }); | |
| const comments = prTimelineResult?.repository?.pullRequest?.timelineItems?.nodes || []; | |
| const rateLimitPattern = /rate limit|API rate limit|secondary rate limit|abuse detection|\b429\b|too many requests/i; | |
| for (const comment of comments) { | |
| if (comment.body && rateLimitPattern.test(comment.body)) { | |
| core.warning(`Rate limiting detected in PR #${pr.number}: ${comment.body.substring(0, 200)}`); | |
| rateLimitDetected = true; | |
| break; | |
| } | |
| } | |
| if (rateLimitDetected) break; | |
| } catch (error) { | |
| core.warning(`Could not check PR #${pr.number} for rate limiting: ${error.message}`); | |
| } | |
| } | |
| if (rateLimitDetected) { | |
| core.warning('🛑 Rate limiting detected in recent PRs. Skipping issue assignment to prevent further rate limit issues.'); | |
| core.setOutput('issue_count', 0); | |
| core.setOutput('issue_numbers', ''); | |
| core.setOutput('issue_list', ''); | |
| core.setOutput('issue_context', ''); | |
| core.setOutput('retry_blocked_list', ''); | |
| core.setOutput('has_issues', 'false'); | |
| return; | |
| } | |
| core.info('✓ No rate limiting detected. Proceeding with issue search.'); | |
| // Labels that indicate an issue should NOT be auto-assigned | |
| const excludeLabels = [ | |
| 'wontfix', | |
| 'duplicate', | |
| 'invalid', | |
| 'question', | |
| 'discussion', | |
| 'needs-discussion', | |
| 'blocked', | |
| 'on-hold', | |
| 'waiting-for-feedback', | |
| 'needs-more-info', | |
| 'no-bot', | |
| 'no-campaign', | |
| // Topics where repeated Copilot attempts were closed without merging. | |
| // A maintainer must remove this label before a new attempt is dispatched. | |
| 'copilot-retry-blocked' | |
| ]; | |
| // Labels that indicate an issue is a GOOD candidate for auto-assignment | |
| const priorityLabels = [ | |
| 'community', | |
| 'good first issue', | |
| 'good-first-issue', | |
| 'bug', | |
| 'enhancement', | |
| 'feature', | |
| 'documentation', | |
| 'tech-debt', | |
| 'refactoring', | |
| 'performance', | |
| 'security' | |
| ]; | |
| // Search for open issues with "cookie" label and without excluded labels | |
| // The "cookie" label indicates issues that are approved work queue items from automated workflows | |
| const query = `is:issue is:open repo:${owner}/${repo} label:cookie -label:"${excludeLabels.join('" -label:"')}"`; | |
| core.info(`Searching: ${query}`); | |
| const response = await github.rest.search.issuesAndPullRequests({ | |
| q: query, | |
| per_page: 100, | |
| sort: 'created', | |
| order: 'desc' | |
| }); | |
| core.info(`Found ${response.data.total_count} total issues matching basic criteria`); | |
| // Fetch full details for each issue to get labels, assignees, sub-issues, and linked PRs | |
| // Track integrity-filtered issues to emit a diagnostic summary | |
| const integrityFilteredIssues = []; | |
| const issuesWithDetails = (await Promise.all( | |
| response.data.items.map(async (issue) => { | |
| // Fetch full issue details — some issues may be blocked by integrity policy | |
| let fullIssue; | |
| try { | |
| fullIssue = await github.rest.issues.get({ | |
| owner, | |
| repo, | |
| issue_number: issue.number | |
| }); | |
| } catch (fetchError) { | |
| // Integrity-filtered issues (403/451) or other transient errors should be | |
| // skipped individually rather than failing the entire batch | |
| const status = fetchError.status || fetchError.response?.status; | |
| // 403 = Forbidden (integrity policy), 451 = Unavailable For Legal Reasons | |
| const isIntegrityBlock = status === 403 || status === 451 || | |
| /\bintegrity\b/i.test(fetchError.message || ''); | |
| const errorSummary = (fetchError.message || String(fetchError)).slice(0, 120); | |
| if (isIntegrityBlock) { | |
| integrityFilteredIssues.push(issue.number); | |
| core.warning(`⚠️ Skipping issue #${issue.number}: blocked by integrity policy (HTTP ${status || 'unknown'}): ${errorSummary}`); | |
| } else { | |
| core.warning(`⚠️ Skipping issue #${issue.number}: could not fetch details (HTTP ${status || 'unknown'}): ${errorSummary}`); | |
| } | |
| return null; | |
| } | |
| // Check if this issue has sub-issues and linked PRs using GraphQL | |
| let subIssuesCount = 0; | |
| let linkedPRs = []; | |
| try { | |
| const issueDetailsQuery = ` | |
| query($owner: String!, $repo: String!, $number: Int!) { | |
| repository(owner: $owner, name: $repo) { | |
| issue(number: $number) { | |
| subIssues { | |
| totalCount | |
| } | |
| timelineItems(first: 100, itemTypes: [CROSS_REFERENCED_EVENT]) { | |
| nodes { | |
| ... on CrossReferencedEvent { | |
| source { | |
| __typename | |
| ... on PullRequest { | |
| number | |
| state | |
| isDraft | |
| author { | |
| login | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| `; | |
| const issueDetailsResult = await github.graphql(issueDetailsQuery, { | |
| owner, | |
| repo, | |
| number: issue.number | |
| }); | |
| subIssuesCount = issueDetailsResult?.repository?.issue?.subIssues?.totalCount || 0; | |
| // Extract linked PRs from timeline | |
| const timelineItems = issueDetailsResult?.repository?.issue?.timelineItems?.nodes || []; | |
| linkedPRs = timelineItems | |
| .filter(item => item?.source?.__typename === 'PullRequest') | |
| .map(item => ({ | |
| number: item.source.number, | |
| state: item.source.state, | |
| isDraft: item.source.isDraft, | |
| author: item.source.author?.login | |
| })); | |
| core.info(`Issue #${issue.number} has ${linkedPRs.length} linked PR(s)`); | |
| } catch (error) { | |
| // If GraphQL query fails, continue with defaults | |
| core.warning(`Could not check details for #${issue.number}: ${error.message}`); | |
| } | |
| return { | |
| ...fullIssue.data, | |
| subIssuesCount, | |
| linkedPRs | |
| }; | |
| }) | |
| )).filter(Boolean); // Remove null entries (integrity-filtered or otherwise skipped) | |
| // Emit diagnostic summary for integrity-filtered issues | |
| if (integrityFilteredIssues.length > 0) { | |
| core.warning(`🛡️ Integrity filter diagnostic: ${integrityFilteredIssues.length} issue(s) were skipped due to integrity policy: #${integrityFilteredIssues.join(', #')}. These issues will be excluded from this run.`); | |
| } | |
| // Pre-flight retry-blocked check: build a map of topics that Copilot already | |
| // attempted and had closed without merging. Repeated attempts on the same topic | |
| // burn a full agent session each time and need a human checkpoint first. | |
| const RETRY_BLOCK_THRESHOLD = 2; | |
| const MIN_TOPIC_LENGTH = 20; | |
| // Normalize a title into a comparable topic key: drop bracketed prefixes | |
| // (e.g. "[WIP] ", "[copilot-opt] "), lowercase, and strip punctuation. | |
| const normalizeTopic = (title) => (title || '') | |
| .replace(/^\s*(\[[^\]]*\]\s*)+/, '') | |
| .toLowerCase() | |
| .replace(/[^a-z0-9]+/g, ' ') | |
| .trim(); | |
| const closedTopicCounts = new Map(); | |
| try { | |
| const closedPRQuery = `is:pr is:closed is:unmerged author:app/copilot-swe-agent repo:${owner}/${repo}`; | |
| const closedPRResponse = await github.rest.search.issuesAndPullRequests({ | |
| q: closedPRQuery, | |
| per_page: 100, | |
| sort: 'created', | |
| order: 'desc' | |
| }); | |
| for (const pr of closedPRResponse.data.items) { | |
| const topic = normalizeTopic(pr.title); | |
| if (topic.length < MIN_TOPIC_LENGTH) continue; | |
| const entry = closedTopicCounts.get(topic) || { count: 0, prs: [] }; | |
| entry.count += 1; | |
| entry.prs.push(pr.number); | |
| closedTopicCounts.set(topic, entry); | |
| } | |
| const blockedTopics = [...closedTopicCounts.entries()].filter(([, v]) => v.count >= RETRY_BLOCK_THRESHOLD); | |
| core.info(`Retry pre-flight: ${closedTopicCounts.size} closed-unmerged Copilot topics, ${blockedTopics.length} at or above the retry-block threshold (${RETRY_BLOCK_THRESHOLD})`); | |
| } catch (error) { | |
| // A failed pre-flight check must not block the run; fall back to no topic data. | |
| core.warning(`Could not build retry-blocked topic map: ${error.message}`); | |
| } | |
| // Returns the blocked topic entry for an issue title, or null when not blocked. | |
| const findRetryBlock = (title) => { | |
| const topic = normalizeTopic(title); | |
| if (topic.length < MIN_TOPIC_LENGTH) return null; | |
| for (const [closedTopic, entry] of closedTopicCounts) { | |
| if (entry.count < RETRY_BLOCK_THRESHOLD) continue; | |
| if (topic === closedTopic || topic.includes(closedTopic) || closedTopic.includes(topic)) { | |
| return entry; | |
| } | |
| } | |
| return null; | |
| }; | |
| // Filter and score issues | |
| const retryBlockedIssues = []; | |
| const scoredIssues = issuesWithDetails | |
| .filter(issue => { | |
| // Exclude issues that already have assignees | |
| if (issue.assignees && issue.assignees.length > 0) { | |
| core.info(`Skipping #${issue.number}: already has assignees`); | |
| return false; | |
| } | |
| // Exclude issues with excluded labels (double check) | |
| const issueLabels = issue.labels.map(l => l.name.toLowerCase()); | |
| if (issueLabels.some(label => excludeLabels.map(l => l.toLowerCase()).includes(label))) { | |
| core.info(`Skipping #${issue.number}: has excluded label`); | |
| return false; | |
| } | |
| // Exclude issues with campaign labels (campaign:*) | |
| // Campaign items are managed by campaign orchestrators | |
| if (issueLabels.some(label => label.startsWith('campaign:'))) { | |
| core.info(`Skipping #${issue.number}: has campaign label (managed by campaign orchestrator)`); | |
| return false; | |
| } | |
| // Exclude issues that have sub-issues (parent/organizing issues) | |
| if (issue.subIssuesCount > 0) { | |
| core.info(`Skipping #${issue.number}: has ${issue.subIssuesCount} sub-issue(s) - parent issues are used for organizing, not tasks`); | |
| return false; | |
| } | |
| // Exclude issues with closed PRs (treat as complete) | |
| const closedPRs = issue.linkedPRs?.filter(pr => pr.state === 'CLOSED' || pr.state === 'MERGED') || []; | |
| if (closedPRs.length > 0) { | |
| core.info(`Skipping #${issue.number}: has ${closedPRs.length} closed/merged PR(s) - treating as complete`); | |
| return false; | |
| } | |
| // Exclude issues with open PRs from Copilot coding agent | |
| const openCopilotPRs = issue.linkedPRs?.filter(pr => | |
| pr.state === 'OPEN' && | |
| (pr.author === 'copilot-swe-agent' || pr.author?.includes('copilot')) | |
| ) || []; | |
| if (openCopilotPRs.length > 0) { | |
| core.info(`Skipping #${issue.number}: has ${openCopilotPRs.length} open PR(s) from Copilot - already being worked on`); | |
| return false; | |
| } | |
| // Block topics that Copilot already attempted twice or more without a merge. | |
| // These require human review before another agent session is spent. | |
| const retryBlock = findRetryBlock(issue.title); | |
| if (retryBlock) { | |
| core.warning(`🛑 Skipping #${issue.number}: retry-blocked topic — ${retryBlock.count} prior Copilot PR(s) closed without merging (#${retryBlock.prs.join(', #')}). Human review required.`); | |
| retryBlockedIssues.push({ number: issue.number, count: retryBlock.count, prs: retryBlock.prs }); | |
| return false; | |
| } | |
| return true; | |
| }) | |
| .map(issue => { | |
| const issueLabels = issue.labels.map(l => l.name.toLowerCase()); | |
| let score = 0; | |
| // Score based on priority labels (higher score = higher priority) | |
| // Community issues always get the highest priority — these are | |
| // requests from external contributors and should be addressed first. | |
| if (issueLabels.includes('community')) { | |
| score += 60; | |
| } | |
| if (issueLabels.includes('good first issue') || issueLabels.includes('good-first-issue')) { | |
| score += 50; | |
| } | |
| if (issueLabels.includes('bug')) { | |
| score += 40; | |
| } | |
| if (issueLabels.includes('security')) { | |
| score += 45; | |
| } | |
| if (issueLabels.includes('documentation')) { | |
| score += 35; | |
| } | |
| if (issueLabels.includes('enhancement') || issueLabels.includes('feature')) { | |
| score += 30; | |
| } | |
| if (issueLabels.includes('performance')) { | |
| score += 25; | |
| } | |
| if (issueLabels.includes('tech-debt') || issueLabels.includes('refactoring')) { | |
| score += 20; | |
| } | |
| // Bonus for issues with clear labels (any priority label) | |
| if (issueLabels.some(label => priorityLabels.map(l => l.toLowerCase()).includes(label))) { | |
| score += 10; | |
| } | |
| // Age bonus: older issues get slight priority (days old / 10) | |
| const ageInDays = Math.floor((Date.now() - new Date(issue.created_at)) / (1000 * 60 * 60 * 24)); | |
| score += Math.min(ageInDays / 10, 20); // Cap age bonus at 20 points | |
| return { | |
| number: issue.number, | |
| title: issue.title, | |
| labels: issue.labels.map(l => l.name), | |
| body: issue.body, | |
| created_at: issue.created_at, | |
| score | |
| }; | |
| }) | |
| .sort((a, b) => b.score - a.score); // Sort by score descending | |
| // Format output | |
| const issueList = scoredIssues.map(i => { | |
| const labelStr = i.labels.length > 0 ? ` [${i.labels.join(', ')}]` : ''; | |
| return `#${i.number}: ${i.title}${labelStr} (score: ${i.score.toFixed(1)})`; | |
| }).join('\n'); | |
| // Pre-fetch compact body context for top candidates so the agent can | |
| // triage without extra reads in most runs. | |
| const issueContext = scoredIssues.slice(0, MAX_ISSUES_WITH_BODY_CONTEXT).map(i => { | |
| const body = (i.body || '').replace(/\s+/g, ' ').trim(); | |
| const bodySnippet = body.length > BODY_SNIPPET_MAX_LENGTH ? `${body.slice(0, BODY_SNIPPET_MAX_LENGTH)}…` : body; | |
| const labelStr = i.labels.length > 0 ? i.labels.join(', ') : 'none'; | |
| return `#${i.number} | score=${i.score.toFixed(1)} | labels=${labelStr}\nTitle: ${i.title}\nBody: ${bodySnippet || '(no body)'}`; | |
| }).join('\n\n---\n\n'); | |
| const issueNumbers = scoredIssues.map(i => i.number).join(','); | |
| core.info(`Total candidate issues after filtering: ${scoredIssues.length}`); | |
| if (scoredIssues.length > 0) { | |
| core.info(`Top candidates:\n${issueList.split('\n').slice(0, 10).join('\n')}`); | |
| } | |
| const retryBlockedList = retryBlockedIssues | |
| .map(i => `#${i.number} | prior closed Copilot PRs: ${i.count} (#${i.prs.join(', #')})`) | |
| .join('\n'); | |
| if (retryBlockedIssues.length > 0) { | |
| core.warning(`🛑 ${retryBlockedIssues.length} issue(s) were retry-blocked and excluded from assignment:\n${retryBlockedList}`); | |
| } | |
| core.setOutput('issue_count', scoredIssues.length); | |
| core.setOutput('issue_numbers', issueNumbers); | |
| core.setOutput('issue_list', issueList); | |
| core.setOutput('issue_context', issueContext); | |
| core.setOutput('retry_blocked_list', retryBlockedList); | |
| if (scoredIssues.length === 0) { | |
| core.info('🍽️ No suitable candidate issues - the plate is empty!'); | |
| core.setOutput('has_issues', 'false'); | |
| } else { | |
| core.setOutput('has_issues', 'true'); | |
| } | |
| } catch (error) { | |
| core.error(`Error searching for issues: ${error.message}`); | |
| core.setOutput('issue_count', 0); | |
| core.setOutput('issue_numbers', ''); | |
| core.setOutput('issue_list', ''); | |
| core.setOutput('issue_context', ''); | |
| core.setOutput('retry_blocked_list', ''); | |
| core.setOutput('has_issues', 'false'); | |
| } | |
| push_evals_state: | |
| needs: | |
| - activation | |
| - evals | |
| if: always() && (!cancelled()) && needs.evals.result != 'skipped' | |
| runs-on: ubuntu-slim | |
| permissions: | |
| contents: write | |
| env: | |
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | |
| steps: | |
| - name: Checkout actions folder | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| repository: github/gh-aw | |
| sparse-checkout: | | |
| actions | |
| clean: false | |
| persist-credentials: false | |
| - name: Setup Scripts | |
| id: setup | |
| uses: ./actions/setup | |
| with: | |
| destination: ${{ runner.temp }}/gh-aw/actions | |
| job-name: ${{ github.job }} | |
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | |
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | |
| env: | |
| GH_AW_SETUP_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-monster.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "0.84.2" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.1" | |
| GH_AW_INFO_ENGINE_ID: "pi" | |
| - name: Checkout repository | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| sparse-checkout: . | |
| - name: Configure Git credentials | |
| env: | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" | |
| - name: Download evals artifact | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| continue-on-error: true | |
| with: | |
| name: evals | |
| path: /tmp/gh-aw/evals-state | |
| - name: Push evals results to git | |
| id: push_evals_state | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GITHUB_RUN_ID: ${{ github.run_id }} | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GH_AW_STATE_DIR: /tmp/gh-aw/evals-state | |
| GH_AW_STATE_BRANCH: evals/issuemonster | |
| GH_AW_STATE_FILES: evals.jsonl | |
| GH_AW_STATE_LABEL: evals results | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/push_experiment_state.cjs'); | |
| await main(); | |
| - name: Restore actions folder | |
| if: always() | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| repository: github/gh-aw | |
| sparse-checkout: | | |
| actions/setup | |
| sparse-checkout-cone-mode: true | |
| clean: false | |
| persist-credentials: false | |
| safe_outputs: | |
| needs: | |
| - activation | |
| - agent | |
| - detection | |
| if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' | |
| runs-on: ubuntu-slim | |
| permissions: | |
| issues: write | |
| pull-requests: write | |
| timeout-minutes: 45 | |
| env: | |
| GH_AW_AGENT_AIC: ${{ needs.agent.outputs.aic }} | |
| GH_AW_AIC: ${{ needs.agent.outputs.aic }} | |
| GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }} | |
| GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/issue-monster" | |
| GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} | |
| GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} | |
| GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} | |
| GH_AW_ENGINE_ID: "pi" | |
| GH_AW_ENGINE_MODEL: "copilot/gpt-5.4" | |
| GH_AW_PROJECT_UTC: "-08:00" | |
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | |
| GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🍪 *Om nom nom by [{workflow_name}]({run_url})*{ai_credits_suffix}{history_link}\",\"runStarted\":\"🍪 ISSUE! ISSUE! [{workflow_name}]({run_url}) hungry for issues on this {event_type}! Om nom nom...\",\"runSuccess\":\"🍪 YUMMY! [{workflow_name}]({run_url}) ate the issues! That was DELICIOUS! Me want MORE! 😋\",\"runFailure\":\"🍪 Aww... [{workflow_name}]({run_url}) {status}. No cookie for monster today... 😢\"}" | |
| GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} | |
| GH_AW_WORKFLOW_EMOJI: "👾" | |
| GH_AW_WORKFLOW_ID: "issue-monster" | |
| GH_AW_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-monster.md" | |
| outputs: | |
| assign_to_agent_assigned: ${{ steps.process_safe_outputs.outputs.assign_to_agent_assigned }} | |
| assign_to_agent_assignment_error_count: ${{ steps.process_safe_outputs.outputs.assign_to_agent_assignment_error_count }} | |
| assign_to_agent_assignment_errors: ${{ steps.process_safe_outputs.outputs.assign_to_agent_assignment_errors }} | |
| code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} | |
| code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} | |
| comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} | |
| comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} | |
| create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} | |
| create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} | |
| process_safe_outputs_items_applied: ${{ steps.process_safe_outputs.outputs.items_applied }} | |
| process_safe_outputs_items_cancelled: ${{ steps.process_safe_outputs.outputs.items_cancelled }} | |
| process_safe_outputs_items_deferred: ${{ steps.process_safe_outputs.outputs.items_deferred }} | |
| process_safe_outputs_items_failed: ${{ steps.process_safe_outputs.outputs.items_failed }} | |
| process_safe_outputs_items_skipped: ${{ steps.process_safe_outputs.outputs.items_skipped }} | |
| process_safe_outputs_items_succeeded: ${{ steps.process_safe_outputs.outputs.items_succeeded }} | |
| process_safe_outputs_items_warnings: ${{ steps.process_safe_outputs.outputs.items_warnings }} | |
| process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} | |
| process_safe_outputs_status: ${{ steps.process_safe_outputs.outputs.status }} | |
| process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} | |
| steps: | |
| - name: Checkout actions folder | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| repository: github/gh-aw | |
| sparse-checkout: | | |
| actions | |
| clean: false | |
| persist-credentials: false | |
| - name: Setup Scripts | |
| id: setup | |
| uses: ./actions/setup | |
| with: | |
| destination: ${{ runner.temp }}/gh-aw/actions | |
| job-name: ${{ github.job }} | |
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | |
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | |
| env: | |
| GH_AW_SETUP_WORKFLOW_NAME: "Issue Monster" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-monster.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "0.84.2" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.1" | |
| GH_AW_INFO_ENGINE_ID: "pi" | |
| - name: Mask OTLP telemetry headers | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" | |
| - name: Download agent output artifact | |
| id: download-agent-output | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| pattern: "{agent,agent-output-fallback}" | |
| merge-multiple: true | |
| path: /tmp/gh-aw/ | |
| - name: Setup agent output environment variable | |
| id: setup-agent-output-env | |
| if: steps.download-agent-output.outcome == 'success' | |
| run: | | |
| mkdir -p /tmp/gh-aw/ | |
| find "/tmp/gh-aw/" -type f -print | |
| if [ -f "/tmp/gh-aw/agent_output.json" ]; then | |
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Configure GH_HOST for enterprise compatibility | |
| id: ghes-host-config | |
| shell: bash | |
| run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input. | |
| # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct | |
| # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. | |
| GH_HOST="${GITHUB_SERVER_URL#https://}" | |
| GH_HOST="${GH_HOST#http://}" | |
| echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" | |
| - name: Process Safe Outputs | |
| id: process_safe_outputs | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | |
| GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} | |
| GH_AW_ALLOWED_DOMAINS: "*.grafana.net,*.sentry.io,api.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GITHUB_API_URL: ${{ github.api_url }} | |
| GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":3,\"target\":\"*\"},\"assign_to_agent\":{\"allowed\":[\"copilot\"],\"ignore-if-error\":true,\"max\":3,\"target\":\"*\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}" | |
| GH_AW_ASSIGN_TO_AGENT_TOKEN: ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/process_safe_outputs.cjs'); | |
| await main(); | |
| - name: Upload Safe Outputs Items | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: safe-outputs-items | |
| path: | | |
| /tmp/gh-aw/safe-output-items.jsonl | |
| /tmp/gh-aw/temporary-id-map.json | |
| if-no-files-found: ignore |