From e1a5e9e3e40f356601fdd9e2e76f4f730b41e00a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 26 Jul 2026 04:30:20 +0000 Subject: [PATCH 1/3] refactor(js): simplify ai_credits_context and action_setup_otlp - Extract duplicate initial state object to EMPTY_AI_CREDITS_STATE constant shared between parseAuditLogCombined and parseAICreditsExceededFromAgentStdio - Remove explicit 'return false' from traverseObjectTree visitor in parseUnknownModelAICreditsFromAuditEntry (undefined is treated identically) - Remove redundant outer 'if (githubEnv)' guard in action_setup_otlp.cjs; writeEnvLine already guards against falsy file paths Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- actions/setup/js/action_setup_otlp.cjs | 14 ++++++-------- actions/setup/js/ai_credits_context.cjs | 16 +++++++--------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/actions/setup/js/action_setup_otlp.cjs b/actions/setup/js/action_setup_otlp.cjs index 374987b25ad..4f3c8873184 100644 --- a/actions/setup/js/action_setup_otlp.cjs +++ b/actions/setup/js/action_setup_otlp.cjs @@ -178,14 +178,12 @@ async function run() { // Always propagate trace/span context to subsequent steps in this job so // that the conclusion span can find the same trace ID. - if (githubEnv) { - if (isValidTraceId(traceId)) writeEnvLine(githubEnv, "GITHUB_AW_OTEL_TRACE_ID", traceId, "GITHUB_AW_OTEL_TRACE_ID", "GITHUB_ENV"); - if (isValidSpanId(spanId)) writeEnvLine(githubEnv, "GITHUB_AW_OTEL_PARENT_SPAN_ID", spanId, "GITHUB_AW_OTEL_PARENT_SPAN_ID", "GITHUB_ENV"); - // Propagate setup-end timestamp so the conclusion span can measure actual - // job execution duration (setup-end → conclusion-start). - const setupEndMs = String(Math.floor(nowMs())); - writeEnvLine(githubEnv, "GITHUB_AW_OTEL_JOB_START_MS", setupEndMs, "GITHUB_AW_OTEL_JOB_START_MS", "GITHUB_ENV"); - } + if (isValidTraceId(traceId)) writeEnvLine(githubEnv, "GITHUB_AW_OTEL_TRACE_ID", traceId, "GITHUB_AW_OTEL_TRACE_ID", "GITHUB_ENV"); + if (isValidSpanId(spanId)) writeEnvLine(githubEnv, "GITHUB_AW_OTEL_PARENT_SPAN_ID", spanId, "GITHUB_AW_OTEL_PARENT_SPAN_ID", "GITHUB_ENV"); + // Propagate setup-end timestamp so the conclusion span can measure actual + // job execution duration (setup-end → conclusion-start). + const setupEndMs = String(Math.floor(nowMs())); + writeEnvLine(githubEnv, "GITHUB_AW_OTEL_JOB_START_MS", setupEndMs, "GITHUB_AW_OTEL_JOB_START_MS", "GITHUB_ENV"); } module.exports = { run }; diff --git a/actions/setup/js/ai_credits_context.cjs b/actions/setup/js/ai_credits_context.cjs index 69f659261fd..0144783b44d 100644 --- a/actions/setup/js/ai_credits_context.cjs +++ b/actions/setup/js/ai_credits_context.cjs @@ -12,6 +12,8 @@ const AI_CREDITS_RATE_LIMIT_ERROR_FIELDS = new Set(["ai_credits_rate_limit_error const AI_CREDITS_RATE_LIMIT_TEXT_FIELDS = new Set(["error", "message", "reason", "details", "detail", "type", "code"]); const AI_CREDITS_RATE_LIMIT_PATTERNS = [/ai[\s_-]*credits?.*(?:rate[\s-]*limit|limit exceeded|budget exceeded|exceeded)/i, /(?:rate[\s-]*limit|too many requests).*(?:ai[\s_-]*credits?)/i, /\bai_credits_limit_exceeded\b/i]; const MAX_AI_CREDITS_EXCEEDED_FIELDS = new Set(["max_ai_credits_exceeded", "maxAiCreditsExceeded"]); +/** @type {{ aiCredits: string, maxAICredits: string, rateLimitError: boolean, maxAICreditsExceeded: boolean }} */ +const EMPTY_AI_CREDITS_STATE = { aiCredits: "", maxAICredits: "", rateLimitError: false, maxAICreditsExceeded: false }; const BUDGET_EXCEEDED_EVENT = "budget_exceeded"; // The literal error type emitted by the AWF API proxy (HTTP 400) when maxAiCredits is active // and the requested model is not in the built-in pricing table. @@ -272,7 +274,6 @@ function parseMaxAICreditsExceededFromAuditLog(auditJsonlPathOverride) { function parseUnknownModelAICreditsFromAuditEntry(entry) { return traverseObjectTree(entry, (_key, value) => { if (value === UNKNOWN_MODEL_AI_CREDITS_TYPE) return true; - return false; }); } @@ -304,9 +305,7 @@ function parseUnknownModelAICreditsFromAuditLog(auditJsonlPathOverride) { * @returns {{ aiCredits: string, maxAICredits: string, rateLimitError: boolean, maxAICreditsExceeded: boolean }} */ function parseAuditLogCombined(auditJsonlPathOverride) { - /** @type {{ aiCredits: string, maxAICredits: string, rateLimitError: boolean, maxAICreditsExceeded: boolean }} */ - const initial = { aiCredits: "", maxAICredits: "", rateLimitError: false, maxAICreditsExceeded: false }; - return iterateAuditEntries(auditJsonlPathOverride, initial, null, (acc, entry) => { + return iterateAuditEntries(auditJsonlPathOverride, EMPTY_AI_CREDITS_STATE, null, (acc, entry) => { const errorInfo = parseAICreditsErrorInfoFromAuditEntry(entry); const max = parseMaxAICreditsFromAuditEntry(entry); const maxAICreditsExceeded = parseMaxAICreditsExceededFromAuditEntry(entry); @@ -376,7 +375,6 @@ function resolveAICreditsFailureState({ logProvenance = true } = {}) { * @returns {{ aiCredits: string, maxAICredits: string, rateLimitError: boolean, maxAICreditsExceeded: boolean }} */ function parseAICreditsExceededFromAgentStdio() { - const initial = { aiCredits: "", maxAICredits: "", rateLimitError: false, maxAICreditsExceeded: false }; try { const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT; // Derive the stdio log path from GH_AW_AGENT_OUTPUT when set, but always @@ -384,11 +382,11 @@ function parseAICreditsExceededFromAgentStdio() { // silently break detection. const derivedPath = agentOutputFile ? path.join(path.dirname(agentOutputFile), "agent-stdio.log") : null; const stdioLogPath = derivedPath && fs.existsSync(derivedPath) ? derivedPath : DEFAULT_AGENT_STDIO_LOG; - if (!fs.existsSync(stdioLogPath)) return initial; + if (!fs.existsSync(stdioLogPath)) return EMPTY_AI_CREDITS_STATE; // Read only the tail to avoid OOM on large logs; the error token always // appears near the end of the file. const stat = fs.statSync(stdioLogPath); - if (stat.size === 0) return initial; + if (stat.size === 0) return EMPTY_AI_CREDITS_STATE; const readSize = Math.min(stat.size, AGENT_STDIO_LOG_MAX_TAIL); const buf = Buffer.alloc(readSize); const fd = fs.openSync(stdioLogPath, "r"); @@ -403,7 +401,7 @@ function parseAICreditsExceededFromAgentStdio() { const RE_G = new RegExp(MAX_AI_CREDITS_EXCEEDED_STDIO_RE.source, "gi"); const allMatches = [...content.matchAll(RE_G)]; const match = allMatches.at(-1); - if (!match) return initial; + if (!match) return EMPTY_AI_CREDITS_STATE; const aiCredits = parsePositiveNumberString(match[1] || ""); const maxAICredits = parsePositiveNumberString(match[2] || ""); return { @@ -413,7 +411,7 @@ function parseAICreditsExceededFromAgentStdio() { maxAICreditsExceeded: true, }; } catch { - return initial; + return EMPTY_AI_CREDITS_STATE; } } From c4cb8d057a319b94b21ee4982d551f0fd6ba6dfc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Jul 2026 10:00:21 +0000 Subject: [PATCH 2/3] fix(js): restore explicit false return in AI credits traversal callback Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- actions/setup/js/ai_credits_context.cjs | 1 + 1 file changed, 1 insertion(+) diff --git a/actions/setup/js/ai_credits_context.cjs b/actions/setup/js/ai_credits_context.cjs index 0144783b44d..5edba535491 100644 --- a/actions/setup/js/ai_credits_context.cjs +++ b/actions/setup/js/ai_credits_context.cjs @@ -274,6 +274,7 @@ function parseMaxAICreditsExceededFromAuditLog(auditJsonlPathOverride) { function parseUnknownModelAICreditsFromAuditEntry(entry) { return traverseObjectTree(entry, (_key, value) => { if (value === UNKNOWN_MODEL_AI_CREDITS_TYPE) return true; + return false; }); } From abb104f54e3389ea5107a415d5c5ae63ddf87c8b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Jul 2026 10:28:20 +0000 Subject: [PATCH 3/3] fix(js): avoid unconditional setup-end timestamp computation Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- actions/setup/js/action_setup_otlp.cjs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/actions/setup/js/action_setup_otlp.cjs b/actions/setup/js/action_setup_otlp.cjs index 4f3c8873184..6681a1de20e 100644 --- a/actions/setup/js/action_setup_otlp.cjs +++ b/actions/setup/js/action_setup_otlp.cjs @@ -182,8 +182,10 @@ async function run() { if (isValidSpanId(spanId)) writeEnvLine(githubEnv, "GITHUB_AW_OTEL_PARENT_SPAN_ID", spanId, "GITHUB_AW_OTEL_PARENT_SPAN_ID", "GITHUB_ENV"); // Propagate setup-end timestamp so the conclusion span can measure actual // job execution duration (setup-end → conclusion-start). - const setupEndMs = String(Math.floor(nowMs())); - writeEnvLine(githubEnv, "GITHUB_AW_OTEL_JOB_START_MS", setupEndMs, "GITHUB_AW_OTEL_JOB_START_MS", "GITHUB_ENV"); + if (githubEnv) { + const setupEndMs = String(Math.floor(nowMs())); + writeEnvLine(githubEnv, "GITHUB_AW_OTEL_JOB_START_MS", setupEndMs, "GITHUB_AW_OTEL_JOB_START_MS", "GITHUB_ENV"); + } } module.exports = { run };