diff --git a/actions/setup/js/handle_agent_failure.cjs b/actions/setup/js/handle_agent_failure.cjs index a111588a7e6..4c2e59bf04b 100644 --- a/actions/setup/js/handle_agent_failure.cjs +++ b/actions/setup/js/handle_agent_failure.cjs @@ -888,18 +888,32 @@ function buildEffectiveTokensRateLimitErrorContext(hasEffectiveTokensRateLimitEr }; const usageLine = effectiveTokens ? `\n- Effective tokens used: \`${formatEffectiveTokensForMessage(effectiveTokens)}\`` : ""; const budgetLine = maxEffectiveTokens ? `\n- Configured ET budget: \`${formatEffectiveTokensForMessage(maxEffectiveTokens)}\`` : ""; - const runLine = runUrl ? `\n- Run: ${runUrl}` : ""; + const runLine = runUrl ? `\n- Run: [${runUrl}](${runUrl})` : ""; const etTableSection = buildETComputationTable(effectiveTokens, readTokenUsageMarkdown()); + const templateName = "effective_tokens_rate_limit_error.md"; + let templatePath = ""; + try { + templatePath = getPromptPath(templateName); + } catch (error) { + throw new Error(`failed to resolve template path for ${templateName} (${getErrorMessage(error)}); ` + "ensure RUNNER_TEMP or GH_AW_PROMPTS_DIR is set and the template file exists"); + } - const etSpecLink = "https://github.github.com/gh-aw/reference/effective-tokens-specification/"; - const tokenOptLink = "https://github.com/github/gh-aw/blob/main/.github/aw/token-optimization.md"; - - return ( - `\n**⛔ Effective Token Budget Exhausted**: The run failed due to effective-token budget/rate-limit enforcement in the API proxy. [What are effective tokens?](${etSpecLink})${usageLine}${budgetLine}${runLine}\n\n` + - etTableSection + - `You can tune this limit with \`max-effective-tokens\` in workflow frontmatter. To reduce token consumption, review the [token optimization guide](${tokenOptLink}).\n` - ); + try { + return ( + "\n" + + renderTemplateFromFile(templatePath, { + et_spec_link: "https://github.github.com/gh-aw/reference/effective-tokens-specification/", + token_opt_link: "https://github.com/github/gh-aw/blob/main/.github/aw/token-optimization.md", + usage_line: usageLine, + budget_line: budgetLine, + run_line: runLine, + et_table_section: etTableSection, + }) + ); + } catch (error) { + throw new Error(`failed to render template at ${templatePath}: ${getErrorMessage(error)}; ` + "verify template syntax and required placeholders: " + "et_spec_link, token_opt_link, usage_line, budget_line, run_line, et_table_section"); + } } /** diff --git a/actions/setup/js/handle_agent_failure.test.cjs b/actions/setup/js/handle_agent_failure.test.cjs index 85088c9193e..9c5bdc4ae82 100644 --- a/actions/setup/js/handle_agent_failure.test.cjs +++ b/actions/setup/js/handle_agent_failure.test.cjs @@ -2007,8 +2007,29 @@ describe("handle_agent_failure", () => { describe("buildEffectiveTokensRateLimitErrorContext", () => { let buildEffectiveTokensRateLimitErrorContext; + const fs = require("fs"); + const os = require("os"); + const path = require("path"); + let tmpDir; beforeEach(() => { + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "gh-aw-test-et-error-context-")); + const promptsDir = path.join(tmpDir, "gh-aw", "prompts"); + fs.mkdirSync(promptsDir, { recursive: true }); + fs.writeFileSync( + path.join(promptsDir, "effective_tokens_rate_limit_error.md"), + "**⛔ Effective Token Budget Exhausted**: The run failed due to effective-token budget/rate-limit enforcement in the API proxy.\n\n" + + "
\n" + + "Why this happened and how to optimize\n\n" + + "- Learn about [effective tokens]({et_spec_link}).\n" + + "{usage_line}{budget_line}{run_line}\n" + + "You can tune this limit with `max-effective-tokens` in workflow frontmatter.\n\n" + + "{et_table_section}\n" + + "- To optimize this workflow, follow the [token optimization instructions]({token_opt_link}).\n" + + "
\n" + ); + process.env.RUNNER_TEMP = tmpDir; + global.core = { info: vi.fn(), warning: vi.fn(), error: vi.fn(), debug: vi.fn(), setOutput: vi.fn(), setFailed: vi.fn() }; global.github = {}; global.context = { repo: { owner: "owner", repo: "repo" } }; @@ -2020,6 +2041,10 @@ describe("handle_agent_failure", () => { delete global.core; delete global.github; delete global.context; + delete process.env.RUNNER_TEMP; + if (tmpDir && fs.existsSync(tmpDir)) { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } }); it("formats effective token values in friendly compact form", () => { @@ -2056,6 +2081,17 @@ describe("handle_agent_failure", () => { expect(result).toContain("https://github.com/github/gh-aw/blob/main/.github/aw/token-optimization.md"); }); + it("formats the run URL as a markdown link", () => { + const result = buildEffectiveTokensRateLimitErrorContext(true, "10000000", "25000000", "https://example.com/run/1"); + expect(result).toContain("- Run: [https://example.com/run/1](https://example.com/run/1)"); + }); + + it("wraps ET guidance in a collapsible details section", () => { + const result = buildEffectiveTokensRateLimitErrorContext(true, "10000000", "25000000", "https://example.com/run/1"); + expect(result).toContain("Why this happened and how to optimize"); + expect(result).toContain("token optimization instructions"); + }); + it("includes a collapsible details section for ET computation", () => { const result = buildEffectiveTokensRateLimitErrorContext(true, "10000000", "25000000", "https://example.com/run/1"); expect(result).toContain("
"); diff --git a/actions/setup/md/effective_tokens_rate_limit_error.md b/actions/setup/md/effective_tokens_rate_limit_error.md new file mode 100644 index 00000000000..eba38e79d7f --- /dev/null +++ b/actions/setup/md/effective_tokens_rate_limit_error.md @@ -0,0 +1,12 @@ +**⛔ Effective Token Budget Exhausted**: The run failed due to effective-token budget/rate-limit enforcement in the API proxy. + +
+Why this happened and how to optimize + +- Learn about [effective tokens]({et_spec_link}). +{usage_line}{budget_line}{run_line} +You can tune this limit with `max-effective-tokens` in workflow frontmatter. + +{et_table_section} +- To optimize this workflow, follow the [token optimization instructions]({token_opt_link}). +