From ca557e130325be4b64bd09b60d06e5355ecaac92 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Aug 2026 06:53:30 +0000 Subject: [PATCH 1/4] Initial plan From 559aa1d0d64140e3aadf037fc86ff049da5cf294 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Aug 2026 07:01:02 +0000 Subject: [PATCH 2/4] initial plan Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/daily-byok-ollama-test.lock.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/daily-byok-ollama-test.lock.yml b/.github/workflows/daily-byok-ollama-test.lock.yml index 857f15a3b9a..38ffad98ff2 100644 --- a/.github/workflows/daily-byok-ollama-test.lock.yml +++ b/.github/workflows/daily-byok-ollama-test.lock.yml @@ -1451,7 +1451,7 @@ jobs: printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt (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.27.43/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,\"targets\":{\"copilot\":{\"host\":\"host.docker.internal:11434\"}}},\"container\":{\"imageTag\":\"0.27.43,squid=sha256:26be5e0b8c8f4c41c8a59126b29bb5d80b07253597472ded2a16bdd75abcbf9d,agent=sha256:04e2d1987a565000a8f114b89d806ae7a3864dd4f944be65275b28c93d8690e6,api-proxy=sha256:d85f57975af5ea23af4996e41ed73fbc8f5b4a47402472bfe82e508f352cb0c1,cli-proxy=sha256:65c45ea2967984d0024f3df61bc71335658a77ede96c8d9665da7a5f33a795ab\"},\"logging\":{\"proxyLogsDir\":\"/tmp/gh-aw/sandbox/firewall/logs\",\"auditDir\":\"/tmp/gh-aw/sandbox/firewall/audit\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" + printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.43/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,\"defaultAiCreditsPricing\":{\"input\":0.000001,\"output\":0.000001},\"targets\":{\"copilot\":{\"host\":\"host.docker.internal:11434\"}},\"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*\"],\"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-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.27.43,squid=sha256:26be5e0b8c8f4c41c8a59126b29bb5d80b07253597472ded2a16bdd75abcbf9d,agent=sha256:04e2d1987a565000a8f114b89d806ae7a3864dd4f944be65275b28c93d8690e6,api-proxy=sha256:d85f57975af5ea23af4996e41ed73fbc8f5b4a47402472bfe82e508f352cb0c1,cli-proxy=sha256:65c45ea2967984d0024f3df61bc71335658a77ede96c8d9665da7a5f33a795ab\"},\"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="" From af258890427adc4020809b0b8b2d906c7180b849 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Aug 2026 07:07:10 +0000 Subject: [PATCH 3/4] fix(eslint): add hasResolvableLocalBinding guard to no-err-stack-then-string-fallback rule Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../no-err-stack-then-string-fallback.test.ts | 48 +++++++++++++++---- .../no-err-stack-then-string-fallback.ts | 46 ++++++++++++++---- 2 files changed, 74 insertions(+), 20 deletions(-) diff --git a/eslint-factory/src/rules/no-err-stack-then-string-fallback.test.ts b/eslint-factory/src/rules/no-err-stack-then-string-fallback.test.ts index bd37491abfb..889a42aa409 100644 --- a/eslint-factory/src/rules/no-err-stack-then-string-fallback.test.ts +++ b/eslint-factory/src/rules/no-err-stack-then-string-fallback.test.ts @@ -64,7 +64,7 @@ describe("no-err-stack-then-string-fallback", () => { valid: [], invalid: [ { - code: `const msg = err instanceof Error ? err.stack : String(err);`, + code: `const { getErrorMessage } = require("./error_helpers.cjs"); const msg = err instanceof Error ? err.stack : String(err);`, errors: [ { messageId: "preferGetErrorMessage", @@ -73,7 +73,7 @@ describe("no-err-stack-then-string-fallback", () => { { messageId: "replaceWithGetErrorMessage", data: { errorVar: "err" }, - output: `const msg = getErrorMessage(err);`, + output: `const { getErrorMessage } = require("./error_helpers.cjs"); const msg = getErrorMessage(err);`, }, ], }, @@ -88,7 +88,7 @@ describe("no-err-stack-then-string-fallback", () => { valid: [], invalid: [ { - code: "core.setFailed(`unhandled error: ${err instanceof Error ? err.stack : String(err)}`);", + code: `const { getErrorMessage } = require("./error_helpers.cjs"); core.setFailed(\`unhandled error: \${err instanceof Error ? err.stack : String(err)}\`);`, errors: [ { messageId: "preferGetErrorMessage", @@ -97,7 +97,7 @@ describe("no-err-stack-then-string-fallback", () => { { messageId: "replaceWithGetErrorMessage", data: { errorVar: "err" }, - output: "core.setFailed(`unhandled error: ${getErrorMessage(err)}`);", + output: `const { getErrorMessage } = require("./error_helpers.cjs"); core.setFailed(\`unhandled error: \${getErrorMessage(err)}\`);`, }, ], }, @@ -119,7 +119,7 @@ describe("no-err-stack-then-string-fallback", () => { valid: [], invalid: [ { - code: `core.setFailed(err && err.stack ? err.stack : String(err));`, + code: `const { getErrorMessage } = require("./error_helpers.cjs"); core.setFailed(err && err.stack ? err.stack : String(err));`, errors: [ { messageId: "preferGetErrorMessage", @@ -128,7 +128,7 @@ describe("no-err-stack-then-string-fallback", () => { { messageId: "replaceWithGetErrorMessage", data: { errorVar: "err" }, - output: `core.setFailed(getErrorMessage(err));`, + output: `const { getErrorMessage } = require("./error_helpers.cjs"); core.setFailed(getErrorMessage(err));`, }, ], }, @@ -143,7 +143,7 @@ describe("no-err-stack-then-string-fallback", () => { valid: [], invalid: [ { - code: `const msg = err && err.stack ? err.stack : String(err);`, + code: `const { getErrorMessage } = require("./error_helpers.cjs"); const msg = err && err.stack ? err.stack : String(err);`, errors: [ { messageId: "preferGetErrorMessage", @@ -152,7 +152,7 @@ describe("no-err-stack-then-string-fallback", () => { { messageId: "replaceWithGetErrorMessage", data: { errorVar: "err" }, - output: `const msg = getErrorMessage(err);`, + output: `const { getErrorMessage } = require("./error_helpers.cjs"); const msg = getErrorMessage(err);`, }, ], }, @@ -167,7 +167,7 @@ describe("no-err-stack-then-string-fallback", () => { valid: [], invalid: [ { - code: `console.error(error && error.stack ? error.stack : String(error));`, + code: `const { getErrorMessage } = require("./error_helpers.cjs"); console.error(error && error.stack ? error.stack : String(error));`, errors: [ { messageId: "preferGetErrorMessage", @@ -176,7 +176,7 @@ describe("no-err-stack-then-string-fallback", () => { { messageId: "replaceWithGetErrorMessage", data: { errorVar: "error" }, - output: `console.error(getErrorMessage(error));`, + output: `const { getErrorMessage } = require("./error_helpers.cjs"); console.error(getErrorMessage(error));`, }, ], }, @@ -185,4 +185,32 @@ describe("no-err-stack-then-string-fallback", () => { ], }); }); + + it("invalid: no getErrorMessage in scope — diagnostic fires but no suggestion offered", () => { + cjsRuleTester.run("no-err-stack-then-string-fallback", noErrStackThenStringFallbackRule, { + valid: [], + invalid: [ + { + code: `const msg = err instanceof Error ? err.stack : String(err);`, + errors: [ + { + messageId: "preferGetErrorMessage", + data: { errorVar: "err" }, + suggestions: [], + }, + ], + }, + { + code: `process.stderr.write(\`[copilot-sdk-driver] unhandled error: \${err instanceof Error ? err.stack : String(err)}\\n\`);`, + errors: [ + { + messageId: "preferGetErrorMessage", + data: { errorVar: "err" }, + suggestions: [], + }, + ], + }, + ], + }); + }); }); diff --git a/eslint-factory/src/rules/no-err-stack-then-string-fallback.ts b/eslint-factory/src/rules/no-err-stack-then-string-fallback.ts index 0af67d2118d..ef526b1687b 100644 --- a/eslint-factory/src/rules/no-err-stack-then-string-fallback.ts +++ b/eslint-factory/src/rules/no-err-stack-then-string-fallback.ts @@ -1,4 +1,4 @@ -import { AST_NODE_TYPES, ESLintUtils, TSESTree } from "@typescript-eslint/utils"; +import { AST_NODE_TYPES, ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; const createRule = ESLintUtils.RuleCreator(name => `https://github.com/github/gh-aw/tree/main/eslint-factory#${name}`); @@ -43,6 +43,15 @@ function isStringErr(node: TSESTree.Node, errVar: string): boolean { return isIdentifierNamed(node.arguments[0], errVar); } +function isDefinitionAvailableAtNode(definition: TSESLint.Scope.Definition, node: TSESTree.Node): boolean { + if (definition.type === "ImportBinding" || definition.type === "FunctionName") { + return true; + } + const definitionNode = definition.name ?? definition.node; + if (!definitionNode?.range || !node.range) return false; + return definitionNode.range[0] < node.range[0]; +} + export const noErrStackThenStringFallbackRule = createRule({ name: "no-err-stack-then-string-fallback", meta: { @@ -63,6 +72,21 @@ export const noErrStackThenStringFallbackRule = createRule({ }, defaultOptions: [], create(context) { + const sourceCode = context.sourceCode; + type SourceCodeScope = ReturnType; + + function hasResolvableLocalBinding(node: TSESTree.Node, name: string): boolean { + let scope: SourceCodeScope | null = sourceCode.getScope(node); + while (scope) { + const variable = scope.set.get(name); + if (variable && variable.defs.some(def => isDefinitionAvailableAtNode(def, node))) { + return true; + } + scope = scope.upper; + } + return false; + } + return { ConditionalExpression(node) { // Patterns: @@ -91,15 +115,17 @@ export const noErrStackThenStringFallbackRule = createRule({ node, messageId: "preferGetErrorMessage", data: { errorVar: errVar }, - suggest: [ - { - messageId: "replaceWithGetErrorMessage", - data: { errorVar: errVar }, - fix(fixer) { - return fixer.replaceText(node, `getErrorMessage(${errVar})`); - }, - }, - ], + suggest: hasResolvableLocalBinding(node, "getErrorMessage") + ? [ + { + messageId: "replaceWithGetErrorMessage", + data: { errorVar: errVar }, + fix(fixer) { + return fixer.replaceText(node, `getErrorMessage(${errVar})`); + }, + }, + ] + : [], }); }, }; From f12e082469feb27b39de7cd5a1de65a695f544d6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Aug 2026 08:04:27 +0000 Subject: [PATCH 4/4] fix(eslint): reject variable definitions in TDZ in isDefinitionAvailableAtNode A binding is inside its own variable declarator's initializer when the reported node's range falls within definition.node's range. Guard against this so that `const getErrorMessage = err instanceof Error ? err.stack : String(err)` does not receive a replaceWithGetErrorMessage suggestion that would produce `const getErrorMessage = getErrorMessage(err)` (TDZ ReferenceError). Add regression test covering the TDZ case. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../no-err-stack-then-string-fallback.test.ts | 18 ++++++++++++++++++ .../rules/no-err-stack-then-string-fallback.ts | 9 ++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/eslint-factory/src/rules/no-err-stack-then-string-fallback.test.ts b/eslint-factory/src/rules/no-err-stack-then-string-fallback.test.ts index 889a42aa409..1c597de23fc 100644 --- a/eslint-factory/src/rules/no-err-stack-then-string-fallback.test.ts +++ b/eslint-factory/src/rules/no-err-stack-then-string-fallback.test.ts @@ -186,6 +186,24 @@ describe("no-err-stack-then-string-fallback", () => { }); }); + it("invalid: getErrorMessage defined in its own initializer (TDZ) — diagnostic fires but no suggestion offered", () => { + cjsRuleTester.run("no-err-stack-then-string-fallback", noErrStackThenStringFallbackRule, { + valid: [], + invalid: [ + { + code: `const getErrorMessage = err instanceof Error ? err.stack : String(err);`, + errors: [ + { + messageId: "preferGetErrorMessage", + data: { errorVar: "err" }, + suggestions: [], + }, + ], + }, + ], + }); + }); + it("invalid: no getErrorMessage in scope — diagnostic fires but no suggestion offered", () => { cjsRuleTester.run("no-err-stack-then-string-fallback", noErrStackThenStringFallbackRule, { valid: [], diff --git a/eslint-factory/src/rules/no-err-stack-then-string-fallback.ts b/eslint-factory/src/rules/no-err-stack-then-string-fallback.ts index ef526b1687b..370af607279 100644 --- a/eslint-factory/src/rules/no-err-stack-then-string-fallback.ts +++ b/eslint-factory/src/rules/no-err-stack-then-string-fallback.ts @@ -49,7 +49,14 @@ function isDefinitionAvailableAtNode(definition: TSESLint.Scope.Definition, node } const definitionNode = definition.name ?? definition.node; if (!definitionNode?.range || !node.range) return false; - return definitionNode.range[0] < node.range[0]; + if (definitionNode.range[0] >= node.range[0]) return false; + // If the node falls inside the variable declarator's range, the binding is in the + // temporal dead zone at that point (e.g. `const getErrorMessage = `). + const declNode = definition.node; + if (declNode?.range && node.range[0] >= declNode.range[0] && node.range[1] <= declNode.range[1]) { + return false; + } + return true; } export const noErrStackThenStringFallbackRule = createRule({