From 51862b0d208ed5e3ba50c449f8fdf76f8d21f8e2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 21:01:48 +0000 Subject: [PATCH 1/6] Initial plan From 0231c3f2c02dbbf0736761f1b6beb3b3d0d534e8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 21:11:33 +0000 Subject: [PATCH 2/6] fix(copilot): pass prompt via --prompt-file to avoid argv expansion limits Agent-Logs-Url: https://github.com/github/gh-aw/sessions/43f03841-a79f-42f0-8065-3a0a2bc6f26e Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --- pkg/workflow/copilot_engine_execution.go | 13 ++++++------ pkg/workflow/copilot_engine_test.go | 20 ++++++++++++------- .../basic-copilot.golden | 2 +- .../with-imports.golden | 2 +- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/pkg/workflow/copilot_engine_execution.go b/pkg/workflow/copilot_engine_execution.go index 2ffd00f0f74..41a71b8e67e 100644 --- a/pkg/workflow/copilot_engine_execution.go +++ b/pkg/workflow/copilot_engine_execution.go @@ -175,8 +175,8 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st } // Build the command - model is always passed via COPILOT_MODEL env var (see env block below). - // The --add-dir "${GITHUB_WORKSPACE}" and --prompt args are appended raw (not through - // shellJoinArgs) because they contain shell variable references that must expand at runtime. + // The --add-dir "${GITHUB_WORKSPACE}" and --prompt-file args are appended raw (not through + // shellJoinArgs) because --add-dir contains a shell variable reference that must expand at runtime. // // When a driver script is provided (GetDriverScriptName), wrap the copilot invocation with // `node ` to enable retry logic for transient CAPIError 400 errors. @@ -196,11 +196,11 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st } if sandboxEnabled { - // Sandbox mode: add workspace dir and inline prompt (read inside AWF container) - copilotCommand = fmt.Sprintf(`%s %s --add-dir "${GITHUB_WORKSPACE}" --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"`, execPrefix, shellJoinArgs(copilotArgs)) + // Sandbox mode: add workspace dir and pass prompt file path directly + copilotCommand = fmt.Sprintf(`%s %s --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt`, execPrefix, shellJoinArgs(copilotArgs)) } else { - // Non-sandbox mode: prompt is read from a shell variable set earlier in the script - copilotCommand = fmt.Sprintf(`%s %s --prompt "$COPILOT_CLI_INSTRUCTION"`, execPrefix, shellJoinArgs(copilotArgs)) + // Non-sandbox mode: pass prompt file path directly + copilotCommand = fmt.Sprintf(`%s %s --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt`, execPrefix, shellJoinArgs(copilotArgs)) } // Conditionally wrap with sandbox (AWF only) @@ -262,7 +262,6 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st command = fmt.Sprintf(`set -o pipefail touch %s (umask 177 && touch %s) -COPILOT_CLI_INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" %s%s 2>&1 | tee %s`, AgentStepSummaryPath, logFile, mkdirCommands.String(), copilotCommand, logFile) } diff --git a/pkg/workflow/copilot_engine_test.go b/pkg/workflow/copilot_engine_test.go index 1e4907bd265..d9ec20e5db9 100644 --- a/pkg/workflow/copilot_engine_test.go +++ b/pkg/workflow/copilot_engine_test.go @@ -126,6 +126,14 @@ func TestCopilotEngineExecutionSteps(t *testing.T) { t.Errorf("Expected command to contain log file name in step content:\n%s", stepContent) } + if !strings.Contains(stepContent, "--prompt-file /tmp/gh-aw/aw-prompts/prompt.txt") { + t.Errorf("Expected command to pass prompt file path directly, got:\n%s", stepContent) + } + + if strings.Contains(stepContent, "COPILOT_CLI_INSTRUCTION=") { + t.Errorf("Expected command to avoid loading prompt into shell variable, got:\n%s", stepContent) + } + if !strings.Contains(stepContent, "COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}") { t.Errorf("Expected COPILOT_GITHUB_TOKEN environment variable in step content:\n%s", stepContent) } @@ -728,7 +736,7 @@ func TestCopilotEngineShellEscaping(t *testing.T) { } } -func TestCopilotEngineInstructionPromptNotEscaped(t *testing.T) { +func TestCopilotEngineUsesPromptFilePath(t *testing.T) { engine := NewCopilotEngine() workflowData := &WorkflowData{ Name: "test-workflow", @@ -760,14 +768,12 @@ func TestCopilotEngineInstructionPromptNotEscaped(t *testing.T) { t.Fatalf("Could not find copilot command in step content:\n%s", stepContent) } - // The $COPILOT_CLI_INSTRUCTION should NOT be wrapped in additional single quotes - if strings.Contains(copilotCommand, `'"$COPILOT_CLI_INSTRUCTION"'`) { - t.Errorf("$COPILOT_CLI_INSTRUCTION should not be wrapped in single quotes: %s", copilotCommand) + if !strings.Contains(copilotCommand, "--prompt-file /tmp/gh-aw/aw-prompts/prompt.txt") { + t.Errorf("Expected prompt to be passed via --prompt-file, got: %s", copilotCommand) } - // The $COPILOT_CLI_INSTRUCTION should remain double-quoted for variable expansion - if !strings.Contains(copilotCommand, `"$COPILOT_CLI_INSTRUCTION"`) { - t.Errorf("$COPILOT_CLI_INSTRUCTION should remain double-quoted: %s", copilotCommand) + if strings.Contains(copilotCommand, "--prompt ") { + t.Errorf("Expected no inline --prompt argument expansion, got: %s", copilotCommand) } } diff --git a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/basic-copilot.golden b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/basic-copilot.golden index a41ef9a2556..330af1896fa 100644 --- a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/basic-copilot.golden +++ b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/basic-copilot.golden @@ -399,7 +399,7 @@ jobs: (umask 177 && touch /tmp/gh-aw/agent-stdio.log) # shellcheck disable=SC1003 sudo -E awf --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" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.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,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --image-tag 0.25.20 --skip-pull --enable-api-proxy \ - -- /bin/bash -c '${GH_AW_NODE_BIN:-node} ${RUNNER_TEMP}/gh-aw/actions/copilot_driver.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + -- /bin/bash -c '${GH_AW_NODE_BIN:-node} ${RUNNER_TEMP}/gh-aw/actions/copilot_driver.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} diff --git a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/with-imports.golden b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/with-imports.golden index c7379ab1678..f4ce9b5fe96 100644 --- a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/with-imports.golden +++ b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/with-imports.golden @@ -400,7 +400,7 @@ jobs: (umask 177 && touch /tmp/gh-aw/agent-stdio.log) # shellcheck disable=SC1003 sudo -E awf --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" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.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,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --image-tag 0.25.20 --skip-pull --enable-api-proxy \ - -- /bin/bash -c '${GH_AW_NODE_BIN:-node} ${RUNNER_TEMP}/gh-aw/actions/copilot_driver.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + -- /bin/bash -c '${GH_AW_NODE_BIN:-node} ${RUNNER_TEMP}/gh-aw/actions/copilot_driver.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} From de5c3d188fc258d57b4c4f0acc0dd654e198b549 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 21:12:31 +0000 Subject: [PATCH 3/6] test(copilot): align prompt-file test naming and comment wording Agent-Logs-Url: https://github.com/github/gh-aw/sessions/43f03841-a79f-42f0-8065-3a0a2bc6f26e Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --- pkg/workflow/copilot_engine_execution.go | 4 ++-- pkg/workflow/copilot_engine_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/workflow/copilot_engine_execution.go b/pkg/workflow/copilot_engine_execution.go index 41a71b8e67e..9a5746ae156 100644 --- a/pkg/workflow/copilot_engine_execution.go +++ b/pkg/workflow/copilot_engine_execution.go @@ -175,8 +175,8 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st } // Build the command - model is always passed via COPILOT_MODEL env var (see env block below). - // The --add-dir "${GITHUB_WORKSPACE}" and --prompt-file args are appended raw (not through - // shellJoinArgs) because --add-dir contains a shell variable reference that must expand at runtime. + // The --add-dir "${GITHUB_WORKSPACE}" arg is appended raw (not through shellJoinArgs) + // because it contains a shell variable reference that must expand at runtime. // // When a driver script is provided (GetDriverScriptName), wrap the copilot invocation with // `node ` to enable retry logic for transient CAPIError 400 errors. diff --git a/pkg/workflow/copilot_engine_test.go b/pkg/workflow/copilot_engine_test.go index d9ec20e5db9..dbab8ea1061 100644 --- a/pkg/workflow/copilot_engine_test.go +++ b/pkg/workflow/copilot_engine_test.go @@ -736,7 +736,7 @@ func TestCopilotEngineShellEscaping(t *testing.T) { } } -func TestCopilotEngineUsesPromptFilePath(t *testing.T) { +func TestCopilotEnginePromptFilePath(t *testing.T) { engine := NewCopilotEngine() workflowData := &WorkflowData{ Name: "test-workflow", From eea7123226a635be99743968a6d66aec720400d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 23:02:36 +0000 Subject: [PATCH 4/6] feat(copilot-driver): add --prompt-file size-aware fallback handling Agent-Logs-Url: https://github.com/github/gh-aw/sessions/21799c47-aabd-44d2-865d-c26e5aaa01e0 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/copilot_driver.cjs | 84 ++++++++++++++++++++++-- actions/setup/js/copilot_driver.test.cjs | 31 +++++++++ 2 files changed, 108 insertions(+), 7 deletions(-) diff --git a/actions/setup/js/copilot_driver.cjs b/actions/setup/js/copilot_driver.cjs index 3f4ebb16e1f..951a12be6d9 100644 --- a/actions/setup/js/copilot_driver.cjs +++ b/actions/setup/js/copilot_driver.cjs @@ -21,7 +21,7 @@ * - Maximum 3 retry attempts after the initial run. * * Usage: node copilot_driver.cjs [args...] - * Example: node copilot_driver.cjs copilot --add-dir /tmp/ --prompt "..." + * Example: node copilot_driver.cjs copilot --add-dir /tmp/ --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt */ "use strict"; @@ -37,6 +37,8 @@ const INITIAL_DELAY_MS = 5000; const BACKOFF_MULTIPLIER = 2; // Maximum delay cap in milliseconds const MAX_DELAY_MS = 60000; +// If prompt files are larger than this threshold, avoid inlining into argv. +const PROMPT_FILE_INLINE_THRESHOLD_BYTES = 100 * 1024; // Pattern to detect transient CAPIError 400 in copilot output const CAPI_ERROR_400_PATTERN = /CAPIError:\s*400/; @@ -167,7 +169,7 @@ function runProcess(command, args, attempt) { return new Promise(resolve => { const startTime = Date.now(); // Redact --prompt value from logs to avoid leaking prompt content - const safeArgs = args.map((arg, i) => (args[i - 1] === "--prompt" ? "" : arg)); + const safeArgs = args.map((arg, i) => (args[i - 1] === "--prompt" || args[i - 1] === "-p" ? "" : arg)); log(`attempt ${attempt + 1}: spawning: ${command} ${safeArgs.join(" ")}`); const child = spawn(command, args, { @@ -235,6 +237,63 @@ function runProcess(command, args, attempt) { }); } +/** + * Build a compact fallback prompt that asks the agent to read instructions from disk. + * @param {string} promptFile + * @returns {string} + */ +function buildPromptFileFallbackInstruction(promptFile) { + return `Read the full instructions from ${promptFile} and execute them exactly as written.`; +} + +/** + * Replace --prompt-file arguments with -p prompt text to support older Copilot CLIs. + * For files over 100KB, emit a compact fallback prompt that instructs the agent to + * read and execute the full prompt file from disk. + * @param {string[]} args + * @returns {string[]} + */ +function resolvePromptFileArgs(args) { + /** @type {string[]} */ + const resolvedArgs = []; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + if (arg !== "--prompt-file") { + resolvedArgs.push(arg); + continue; + } + + const promptFile = args[i + 1]; + if (!promptFile) { + log("warning: --prompt-file provided without a path; leaving arguments unchanged"); + resolvedArgs.push(arg); + continue; + } + + try { + const stat = fs.statSync(promptFile); + log(`resolved --prompt-file: path=${promptFile} size=${stat.size}B`); + + if (stat.size > PROMPT_FILE_INLINE_THRESHOLD_BYTES) { + log(`prompt file exceeds ${PROMPT_FILE_INLINE_THRESHOLD_BYTES}B; using compact fallback prompt`); + resolvedArgs.push("-p", buildPromptFileFallbackInstruction(promptFile)); + } else { + const promptText = fs.readFileSync(promptFile, "utf8"); + resolvedArgs.push("-p", promptText); + } + i++; // Skip the prompt-file path argument + } catch (error) { + const err = /** @type {Error} */ error; + log(`warning: failed to resolve --prompt-file ${promptFile}: ${err.message}; leaving arguments unchanged`); + resolvedArgs.push(arg, promptFile); + i++; // Skip the prompt-file path argument + } + } + + return resolvedArgs; +} + /** * Main entry point: run copilot with retry logic for partially-executed sessions. */ @@ -249,6 +308,7 @@ async function main() { log(`starting: command=${command} maxRetries=${MAX_RETRIES} initialDelayMs=${INITIAL_DELAY_MS}` + ` backoffMultiplier=${BACKOFF_MULTIPLIER} maxDelayMs=${MAX_DELAY_MS}` + ` nodeVersion=${process.version} platform=${process.platform}`); await checkCommandAccessible(command); + const resolvedArgs = resolvePromptFileArgs(args); let delay = INITIAL_DELAY_MS; let lastExitCode = 1; @@ -256,7 +316,7 @@ async function main() { for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) { // Add --continue flag on retries so the copilot session continues from where it left off - const currentArgs = attempt > 0 ? [...args, "--continue"] : args; + const currentArgs = attempt > 0 ? [...resolvedArgs, "--continue"] : resolvedArgs; if (attempt > 0) { log(`retry ${attempt}/${MAX_RETRIES}: sleeping ${delay}ms before next attempt with --continue`); @@ -335,7 +395,17 @@ async function main() { process.exit(lastExitCode); } -main().catch(err => { - log(`unexpected error: ${err.message}`); - process.exit(1); -}); +if (typeof module !== "undefined" && module.exports) { + module.exports = { + PROMPT_FILE_INLINE_THRESHOLD_BYTES, + buildPromptFileFallbackInstruction, + resolvePromptFileArgs, + }; +} + +if (require.main === module) { + main().catch(err => { + log(`unexpected error: ${err.message}`); + process.exit(1); + }); +} diff --git a/actions/setup/js/copilot_driver.test.cjs b/actions/setup/js/copilot_driver.test.cjs index 12eae0e31e5..ed413e63cc7 100644 --- a/actions/setup/js/copilot_driver.test.cjs +++ b/actions/setup/js/copilot_driver.test.cjs @@ -1,4 +1,11 @@ import { describe, it, expect } from "vitest"; +import { createRequire } from "module"; +import fs from "fs"; +import os from "os"; +import path from "path"; + +const require = createRequire(import.meta.url); +const { resolvePromptFileArgs, buildPromptFileFallbackInstruction, PROMPT_FILE_INLINE_THRESHOLD_BYTES } = require("./copilot_driver.cjs"); describe("copilot_driver.cjs", () => { // Test the core logic patterns used by the driver without importing the module @@ -284,6 +291,30 @@ describe("copilot_driver.cjs", () => { }); }); + describe("prompt-file support", () => { + it("inlines small prompt files as -p", () => { + const promptFile = path.join(os.tmpdir(), `copilot-driver-small-${Date.now()}.txt`); + fs.writeFileSync(promptFile, "small prompt body", "utf8"); + + const resolved = resolvePromptFileArgs(["--add-dir", "/tmp", "--prompt-file", promptFile, "--allow-all-tools"]); + expect(resolved).toEqual(["--add-dir", "/tmp", "-p", "small prompt body", "--allow-all-tools"]); + }); + + it("uses compact fallback prompt when prompt file is larger than 100KB", () => { + const promptFile = path.join(os.tmpdir(), `copilot-driver-large-${Date.now()}.txt`); + fs.writeFileSync(promptFile, "x".repeat(PROMPT_FILE_INLINE_THRESHOLD_BYTES + 1), "utf8"); + + const resolved = resolvePromptFileArgs(["--prompt-file", promptFile, "--allow-all-tools"]); + expect(resolved).toEqual(["-p", buildPromptFileFallbackInstruction(promptFile), "--allow-all-tools"]); + }); + + it("keeps --prompt-file arguments unchanged when file resolution fails", () => { + const missingPath = path.join(os.tmpdir(), `copilot-driver-missing-${Date.now()}.txt`); + const resolved = resolvePromptFileArgs(["--prompt-file", missingPath, "--allow-all-tools"]); + expect(resolved).toEqual(["--prompt-file", missingPath, "--allow-all-tools"]); + }); + }); + describe("formatDuration", () => { // Inline the same logic as the driver's formatDuration for unit testing function formatDuration(ms) { From c9340942ba6481bfa9cc6a9ea0ab4d0e086aeb18 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 23:03:32 +0000 Subject: [PATCH 5/6] chore(copilot-driver): improve prompt-file threshold logging and edge handling Agent-Logs-Url: https://github.com/github/gh-aw/sessions/21799c47-aabd-44d2-865d-c26e5aaa01e0 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/copilot_driver.cjs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/actions/setup/js/copilot_driver.cjs b/actions/setup/js/copilot_driver.cjs index 951a12be6d9..1ff3605c6f3 100644 --- a/actions/setup/js/copilot_driver.cjs +++ b/actions/setup/js/copilot_driver.cjs @@ -39,6 +39,7 @@ const BACKOFF_MULTIPLIER = 2; const MAX_DELAY_MS = 60000; // If prompt files are larger than this threshold, avoid inlining into argv. const PROMPT_FILE_INLINE_THRESHOLD_BYTES = 100 * 1024; +const PROMPT_FILE_INLINE_THRESHOLD_LABEL = "100KB"; // Pattern to detect transient CAPIError 400 in copilot output const CAPI_ERROR_400_PATTERN = /CAPIError:\s*400/; @@ -264,19 +265,19 @@ function resolvePromptFileArgs(args) { continue; } - const promptFile = args[i + 1]; - if (!promptFile) { + if (i + 1 >= args.length) { log("warning: --prompt-file provided without a path; leaving arguments unchanged"); resolvedArgs.push(arg); continue; } + const promptFile = args[i + 1]; try { const stat = fs.statSync(promptFile); log(`resolved --prompt-file: path=${promptFile} size=${stat.size}B`); if (stat.size > PROMPT_FILE_INLINE_THRESHOLD_BYTES) { - log(`prompt file exceeds ${PROMPT_FILE_INLINE_THRESHOLD_BYTES}B; using compact fallback prompt`); + log(`prompt file exceeds ${PROMPT_FILE_INLINE_THRESHOLD_LABEL}; using compact fallback prompt`); resolvedArgs.push("-p", buildPromptFileFallbackInstruction(promptFile)); } else { const promptText = fs.readFileSync(promptFile, "utf8"); From 9f3d6be2d07b6371b6fdac55770965f842d5bd1d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 23:12:45 +0000 Subject: [PATCH 6/6] Add changeset --- .changeset/patch-use-prompt-file-for-copilot.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/patch-use-prompt-file-for-copilot.md diff --git a/.changeset/patch-use-prompt-file-for-copilot.md b/.changeset/patch-use-prompt-file-for-copilot.md new file mode 100644 index 00000000000..3ee4c095fe2 --- /dev/null +++ b/.changeset/patch-use-prompt-file-for-copilot.md @@ -0,0 +1,5 @@ +--- +"gh-aw": patch +--- + +Use `--prompt-file` for Copilot execution so large prompts do not exceed Linux `ARG_MAX` and fail before agent startup.