From d4b152ade869b0163ae4b34a5c193a8de7e7954a Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 25 Aug 2026 02:45:27 +0200 Subject: [PATCH 1/7] Fix full local test suites --- Makefile | 2 + actions/setup/js/add_comment.test.cjs | 17 +- actions/setup/js/awf_reflect.cjs | 9 +- actions/setup/js/awf_reflect.test.cjs | 23 ++ actions/setup/js/claude_harness.test.cjs | 22 +- actions/setup/js/copilot_harness.test.cjs | 38 +-- .../js/generate_usage_activity_summary.cjs | 2 +- .../setup/js/notify_comment_error.test.cjs | 4 +- actions/setup/js/package-lock.json | 251 ++++++++++-------- actions/setup/js/package.json | 1 + .../safe_outputs_mcp_server_defaults.test.cjs | 16 +- pkg/cli/install_copilot_cli_test.go | 9 + scripts/agent-report-progress.sh | 40 ++- 13 files changed, 295 insertions(+), 139 deletions(-) diff --git a/Makefile b/Makefile index dbe9e66b093..00c3030e19f 100644 --- a/Makefile +++ b/Makefile @@ -259,6 +259,7 @@ check-cjs-syntax: .PHONY: test-js test-js: build-js cd actions/setup/js && npm run test:js -- --no-file-parallelism + cd eslint-factory && npm test # Test impacted JavaScript unit tests only (excluding integration tests) .PHONY: test-impacted-js @@ -806,6 +807,7 @@ deps: check-node-version go mod download go mod tidy cd actions/setup/js && npm ci + cd eslint-factory && npm ci # Install development tools (including linter) .PHONY: deps-dev diff --git a/actions/setup/js/add_comment.test.cjs b/actions/setup/js/add_comment.test.cjs index 32b0d906c5c..420c16e9af8 100644 --- a/actions/setup/js/add_comment.test.cjs +++ b/actions/setup/js/add_comment.test.cjs @@ -1,5 +1,5 @@ // @ts-check -import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { describe, it, expect, beforeAll, beforeEach, afterEach, afterAll } from "vitest"; import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; @@ -7,7 +7,20 @@ import { syncRuntimePromptTemplates } from "./test_prompt_templates.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -syncRuntimePromptTemplates(import.meta.url); +const { runtimePromptsDir } = syncRuntimePromptTemplates(import.meta.url); +const originalPromptsDir = process.env.GH_AW_PROMPTS_DIR; + +beforeAll(() => { + process.env.GH_AW_PROMPTS_DIR = runtimePromptsDir; +}); + +afterAll(() => { + if (originalPromptsDir === undefined) { + delete process.env.GH_AW_PROMPTS_DIR; + } else { + process.env.GH_AW_PROMPTS_DIR = originalPromptsDir; + } +}); describe("add_comment", () => { let mockCore; diff --git a/actions/setup/js/awf_reflect.cjs b/actions/setup/js/awf_reflect.cjs index 437d694f44d..15e7a712cf0 100644 --- a/actions/setup/js/awf_reflect.cjs +++ b/actions/setup/js/awf_reflect.cjs @@ -32,7 +32,7 @@ const AWF_API_PROXY_REFLECT_URL = "http://api-proxy:10000/reflect"; // Persist outside the read-only gh-aw infrastructure mount. const AWF_REFLECT_OUTPUT_PATH = path.join(process.env.RUNNER_TEMP || os.tmpdir(), "awf-reflect.json"); // Milliseconds to wait for the /reflect endpoint before giving up. -const AWF_REFLECT_TIMEOUT_MS = 60000; +const AWF_REFLECT_TIMEOUT_MS = Number.parseInt(process.env.GH_AW_REFLECT_TIMEOUT_MS || "60000", 10); // Milliseconds to wait for each models_url fallback fetch (shorter than the main reflect timeout). const AWF_MODELS_URL_TIMEOUT_MS = 3000; // Milliseconds to wait for an api-proxy provider listener to accept a real TCP connection. @@ -367,7 +367,7 @@ async function enrichReflectModels(reflectData, timeoutMs, logger) { * outputPath: string, * bytesWritten?: number, * reflectData?: object, - * reason?: "unexpected_status"|"timeout"|"request_failed", + * reason?: "disabled"|"unexpected_status"|"timeout"|"request_failed", * status?: number, * error?: string, * }>} @@ -380,6 +380,11 @@ async function fetchAWFReflect(options) { const logger = (options && options.logger) || DEFAULT_REFLECT_LOGGER; const writeFile = (options && options.writeFileSync) || fs.writeFileSync; + if (process.env.GH_AW_SKIP_REFLECT === "true") { + logger("awf-reflect: disabled by GH_AW_SKIP_REFLECT"); + return { ok: false, reflectUrl, outputPath, reason: "disabled" }; + } + logger(`awf-reflect: fetching ${reflectUrl} (timeout=${timeoutMs}ms)`); const ac = new AbortController(); diff --git a/actions/setup/js/awf_reflect.test.cjs b/actions/setup/js/awf_reflect.test.cjs index 483a030a27f..29c36c0af79 100644 --- a/actions/setup/js/awf_reflect.test.cjs +++ b/actions/setup/js/awf_reflect.test.cjs @@ -639,6 +639,29 @@ describe("awf_reflect.cjs", () => { describe("fetchAWFReflect", () => { afterEach(() => { vi.unstubAllGlobals(); + vi.unstubAllEnvs(); + }); + + it("skips network requests when reflection is disabled", async () => { + const fetchMock = vi.fn(); + const logs = []; + vi.stubGlobal("fetch", fetchMock); + vi.stubEnv("GH_AW_SKIP_REFLECT", "true"); + + await expect( + fetchAWFReflect({ + reflectUrl: "http://api-proxy:10000/reflect", + outputPath: "/tmp/gh-aw-test-noop.json", + logger: msg => logs.push(msg), + }) + ).resolves.toEqual({ + ok: false, + reflectUrl: "http://api-proxy:10000/reflect", + outputPath: "/tmp/gh-aw-test-noop.json", + reason: "disabled", + }); + expect(fetchMock).not.toHaveBeenCalled(); + expect(logs).toContain("awf-reflect: disabled by GH_AW_SKIP_REFLECT"); }); it("saves enriched reflect data when api-proxy returns null models for configured provider", async () => { diff --git a/actions/setup/js/claude_harness.test.cjs b/actions/setup/js/claude_harness.test.cjs index ebc4d65494c..014ef09c75f 100644 --- a/actions/setup/js/claude_harness.test.cjs +++ b/actions/setup/js/claude_harness.test.cjs @@ -30,6 +30,12 @@ const { } = require("./claude_harness.cjs"); const agentTempDir = "/tmp/gh-aw/agent"; +const harnessChildEnv = { + ...process.env, + GH_AW_HARNESS_INITIAL_DELAY_MS: "1", + GH_AW_HARNESS_MAX_DELAY_MS: "1", + GH_AW_SKIP_REFLECT: "true", +}; function makeHarnessTempDir(name) { fs.mkdirSync(agentTempDir, { recursive: true }); @@ -46,7 +52,7 @@ function runHarnessWithStub({ stubScript, prompt = "fix the bug", extraArgs = [] const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", ...extraArgs, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./claude_harness.cjs")), - env: { ...process.env, ...extraEnv, CLAUDE_HARNESS_STUB_CALLS: callsPath }, + env: { ...harnessChildEnv, ...extraEnv, CLAUDE_HARNESS_STUB_CALLS: callsPath }, encoding: "utf8", timeout: 45000, }); @@ -896,7 +902,7 @@ process.exit(0);`, const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./claude_harness.cjs")), - env: { ...process.env, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath }, + env: { ...harnessChildEnv, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath }, encoding: "utf8", timeout: 10000, }); @@ -928,7 +934,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./claude_harness.cjs")), - env: { ...process.env, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath }, + env: { ...harnessChildEnv, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath }, encoding: "utf8", timeout: 10000, }); @@ -975,7 +981,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./claude_harness.cjs")), - env: { ...process.env, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath }, + env: { ...harnessChildEnv, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath }, encoding: "utf8", timeout: 10000, }); @@ -1008,7 +1014,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./claude_harness.cjs")), - env: { ...process.env, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath }, + env: { ...harnessChildEnv, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath }, encoding: "utf8", timeout: 10000, }); @@ -1040,7 +1046,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./claude_harness.cjs")), env: { - ...process.env, + ...harnessChildEnv, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath, @@ -1077,7 +1083,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./claude_harness.cjs")), env: { - ...process.env, + ...harnessChildEnv, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_HARNESS_MAX_RETRIES: "0", @@ -1112,7 +1118,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./claude_harness.cjs")), env: { - ...process.env, + ...harnessChildEnv, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_HARNESS_MAX_RETRIES: "0", diff --git a/actions/setup/js/copilot_harness.test.cjs b/actions/setup/js/copilot_harness.test.cjs index 3d20dd50e7f..fc99af63d09 100644 --- a/actions/setup/js/copilot_harness.test.cjs +++ b/actions/setup/js/copilot_harness.test.cjs @@ -71,6 +71,12 @@ const { const { detectNonRetryableHarnessGuard, buildSoftTimeoutGuard } = require("./harness_retry_guard.cjs"); const agentTempDir = "/tmp/gh-aw/agent"; +const harnessChildEnv = { + ...process.env, + GH_AW_HARNESS_INITIAL_DELAY_MS: "1", + GH_AW_HARNESS_MAX_DELAY_MS: "1", + GH_AW_SKIP_REFLECT: "true", +}; function makeHarnessTempDir(name) { fs.mkdirSync(agentTempDir, { recursive: true }); @@ -2617,7 +2623,7 @@ process.exit(0);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), - env: { ...process.env, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath }, + env: { ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath }, encoding: "utf8", timeout: 10000, }); @@ -2649,7 +2655,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), - env: { ...process.env, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath }, + env: { ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath }, encoding: "utf8", timeout: 10000, }); @@ -2686,7 +2692,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), env: { - ...process.env, + ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_SAFEOUTPUTS_CLI: "true", @@ -2728,7 +2734,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), - env: { ...process.env, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath }, + env: { ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath }, encoding: "utf8", timeout: 15000, }); @@ -2760,7 +2766,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), - env: { ...process.env, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_SAFEOUTPUTS_CLI: "true" }, + env: { ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_SAFEOUTPUTS_CLI: "true" }, encoding: "utf8", timeout: 15000, }); @@ -2791,7 +2797,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), - env: { ...process.env, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath }, + env: { ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath }, encoding: "utf8", timeout: 15000, }); @@ -2830,7 +2836,7 @@ setInterval(() => {}, 1000);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), env: { - ...process.env, + ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_HARNESS_WATCHDOG_TIMEOUT_MS: "100", @@ -2870,7 +2876,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), env: { - ...process.env, + ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, // Override retry config to keep the test fast. @@ -2914,7 +2920,7 @@ setInterval(() => {}, 1000);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), env: { - ...process.env, + ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_HARNESS_WATCHDOG_TIMEOUT_MS: "100", @@ -2958,7 +2964,7 @@ setInterval(() => {}, 1000);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), env: { - ...process.env, + ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_HARNESS_WATCHDOG_TIMEOUT_MS: "100", @@ -2996,7 +3002,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), env: { - ...process.env, + ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, }, @@ -3043,7 +3049,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), - env: { ...process.env, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath }, + env: { ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath }, encoding: "utf8", timeout: 10000, }); @@ -3076,7 +3082,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), - env: { ...process.env, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath }, + env: { ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath }, encoding: "utf8", timeout: 10000, }); @@ -3107,7 +3113,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), env: { - ...process.env, + ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_HARNESS_MAX_RETRIES: "0", @@ -3144,7 +3150,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), env: { - ...process.env, + ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath, @@ -3178,7 +3184,7 @@ process.exit(1);`, const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], { cwd: path.dirname(require.resolve("./copilot_harness.cjs")), env: { - ...process.env, + ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_HARNESS_MAX_RETRIES: "0", diff --git a/actions/setup/js/generate_usage_activity_summary.cjs b/actions/setup/js/generate_usage_activity_summary.cjs index 20c31939bc5..663e540f9c8 100644 --- a/actions/setup/js/generate_usage_activity_summary.cjs +++ b/actions/setup/js/generate_usage_activity_summary.cjs @@ -11,7 +11,7 @@ // working_set: cumulative input-token traffic relative to peak invocation input const fs = require("fs"); -const { globSync } = require("node:fs"); +const { globSync } = require("glob"); const path = require("path"); const { readExperimentAssignments } = require("./experiment_helpers.cjs"); const { calculateWorkingSetFromJSONL } = require("./working_set_metrics.cjs"); diff --git a/actions/setup/js/notify_comment_error.test.cjs b/actions/setup/js/notify_comment_error.test.cjs index 6bf3d60d8d4..b0720afd1ce 100644 --- a/actions/setup/js/notify_comment_error.test.cjs +++ b/actions/setup/js/notify_comment_error.test.cjs @@ -48,6 +48,7 @@ const mockCore = { (originalEnv = { GH_AW_COMMENT_ID: process.env.GH_AW_COMMENT_ID, GH_AW_COMMENT_REPO: process.env.GH_AW_COMMENT_REPO, + GH_AW_PROMPTS_DIR: process.env.GH_AW_PROMPTS_DIR, GH_AW_RUN_URL: process.env.GH_AW_RUN_URL, GH_AW_WORKFLOW_NAME: process.env.GH_AW_WORKFLOW_NAME, GH_AW_AGENT_CONCLUSION: process.env.GH_AW_AGENT_CONCLUSION, @@ -60,7 +61,8 @@ const mockCore = { GH_AW_OUTPUT_CREATE_ISSUE_ISSUE_URL: process.env.GH_AW_OUTPUT_CREATE_ISSUE_ISSUE_URL, GH_AW_OUTPUT_ADD_COMMENT_COMMENT_URL: process.env.GH_AW_OUTPUT_ADD_COMMENT_COMMENT_URL, GH_AW_OUTPUT_CREATE_PULL_REQUEST_PULL_REQUEST_URL: process.env.GH_AW_OUTPUT_CREATE_PULL_REQUEST_PULL_REQUEST_URL, - })); + }), + (process.env.GH_AW_PROMPTS_DIR = path.join(process.cwd(), "../md"))); const scriptPath = path.join(process.cwd(), "notify_comment_error.cjs"); notifyCommentScript = fs.readFileSync(scriptPath, "utf8"); }), diff --git a/actions/setup/js/package-lock.json b/actions/setup/js/package-lock.json index f5844e065e8..aac5a009983 100644 --- a/actions/setup/js/package-lock.json +++ b/actions/setup/js/package-lock.json @@ -16,6 +16,7 @@ "@types/node": "^26.2.0", "@vitest/coverage-v8": "^4.1.8", "@vitest/ui": "^4.1.10", + "glob": "^11.0.3", "minimatch": ">=10.2.6", "prettier": "^3.9.6", "typescript": "^7.0.2", @@ -643,9 +644,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -663,9 +661,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -683,9 +678,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -703,9 +695,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -766,21 +755,13 @@ } }, "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, + "license": "BlueOak-1.0.0", "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@jridgewell/resolve-uri": { @@ -1397,9 +1378,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1417,9 +1395,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1437,9 +1412,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1457,9 +1429,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1477,9 +1446,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1497,9 +1463,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2174,9 +2137,9 @@ } }, "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", "dev": true, "license": "MIT", "engines": { @@ -2250,6 +2213,112 @@ "node": ">= 14" } }, + "node_modules/archiver-utils/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/archiver-utils/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/archiver-utils/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/archiver-utils/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -2866,48 +2935,25 @@ } }, "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", - "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -3070,19 +3116,19 @@ } }, "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/js-tokens": { @@ -3326,9 +3372,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -3350,9 +3393,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -3374,9 +3414,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -3398,9 +3435,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -3464,11 +3498,14 @@ "license": "MIT" }, "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", "dev": true, - "license": "ISC" + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } }, "node_modules/magic-string": { "version": "0.30.21", @@ -3648,17 +3685,17 @@ } }, "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" diff --git a/actions/setup/js/package.json b/actions/setup/js/package.json index f6283f6f46b..0dab06cba80 100644 --- a/actions/setup/js/package.json +++ b/actions/setup/js/package.json @@ -11,6 +11,7 @@ "@types/node": "^26.2.0", "@vitest/coverage-v8": "^4.1.8", "@vitest/ui": "^4.1.10", + "glob": "^11.0.3", "minimatch": ">=10.2.6", "prettier": "^3.9.6", "typescript": "^7.0.2", diff --git a/actions/setup/js/safe_outputs_mcp_server_defaults.test.cjs b/actions/setup/js/safe_outputs_mcp_server_defaults.test.cjs index e1d0d144e8e..eef109bbf1f 100644 --- a/actions/setup/js/safe_outputs_mcp_server_defaults.test.cjs +++ b/actions/setup/js/safe_outputs_mcp_server_defaults.test.cjs @@ -1,8 +1,22 @@ -import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { describe, it, expect, beforeEach, afterEach, afterAll, vi } from "vitest"; import fs from "fs"; +import os from "os"; import path from "path"; import { spawn } from "child_process"; +const originalRunnerTemp = process.env.RUNNER_TEMP; +const localRunnerTemp = originalRunnerTemp ? null : fs.mkdtempSync(path.join(os.tmpdir(), "gh-aw-runner-temp-")); +if (localRunnerTemp) { + process.env.RUNNER_TEMP = localRunnerTemp; +} + +afterAll(() => { + if (localRunnerTemp) { + fs.rmSync(localRunnerTemp, { recursive: true, force: true }); + delete process.env.RUNNER_TEMP; + } +}); + // Check if ${RUNNER_TEMP}/gh-aw/safeoutputs is writable (only available in agent container) function canWriteToDefaultPath() { try { diff --git a/pkg/cli/install_copilot_cli_test.go b/pkg/cli/install_copilot_cli_test.go index 39d28d3c738..fd5b9f79091 100644 --- a/pkg/cli/install_copilot_cli_test.go +++ b/pkg/cli/install_copilot_cli_test.go @@ -95,12 +95,21 @@ func TestInstallCopilotCLIScriptPreservesCachedBinaryAtInstallPath(t *testing.T) cachedCopilot := filepath.Join(toolcacheBin, "copilot") cachedContents := []byte("#!/usr/bin/env bash\necho 'copilot 1.2.3 preserved'\n") require.NoError(t, os.WriteFile(cachedCopilot, cachedContents, 0o755)) + fakeBinDir := filepath.Join(tempDir, "fake-bin") + require.NoError(t, os.MkdirAll(fakeBinDir, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(fakeBinDir, "sudo"), []byte(`#!/usr/bin/env bash +if [ "${1:-}" = "chown" ]; then + exit 0 +fi +exec "$@" +`), 0o755)) cmd := exec.Command("bash", installScript, "1.2.3") cmd.Env = append(os.Environ(), "RUNNER_TOOL_CACHE="+filepath.Join(tempDir, "toolcache"), "GITHUB_PATH="+filepath.Join(tempDir, "github-path"), "COPILOT_INSTALL_DIR="+toolcacheBin, + "PATH="+fakeBinDir+":"+os.Getenv("PATH"), ) output, err := cmd.CombinedOutput() diff --git a/scripts/agent-report-progress.sh b/scripts/agent-report-progress.sh index 77c94b0c2cb..1ed2a1cc78a 100755 --- a/scripts/agent-report-progress.sh +++ b/scripts/agent-report-progress.sh @@ -168,7 +168,45 @@ lint_go_packages() { } lint_custom_go_packages() { - make --no-print-directory golint-custom LINTER_PACKAGES="${go_packages[*]}" + local output + local status + local diagnostic_found=0 + local relevant_diagnostic_found=0 + local line + local diagnostic_file + local changed_file + + set +e + output=$(make --no-print-directory golint-custom LINTER_PACKAGES="${go_packages[*]}" 2>&1) + status=$? + set -e + + if [ "$status" -eq 0 ]; then + printf '%s\n' "$output" + return 0 + fi + + while IFS= read -r line; do + if [[ "$line" =~ ^([^:]+\.go):[0-9]+:[0-9]+: ]]; then + diagnostic_found=1 + diagnostic_file="${BASH_REMATCH[1]}" + for changed_file in "${go_files[@]}"; do + if [ "$diagnostic_file" = "$changed_file" ]; then + printf '%s\n' "$line" + relevant_diagnostic_found=1 + break + fi + done + else + printf '%s\n' "$line" + fi + done <<< "$output" + + if [ "$relevant_diagnostic_found" -eq 1 ] || [ "$diagnostic_found" -eq 0 ]; then + return "$status" + fi + + echo "Custom Go linter diagnostics were limited to unchanged files; skipping them for this change-scoped gate." } lint_javascript() { From 7b225fb197242fb4c5347844d696cb4e29b0e77a Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 25 Aug 2026 03:02:04 +0200 Subject: [PATCH 2/7] Fix workflow logs timeout test --- pkg/cli/context_cancellation_test.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/pkg/cli/context_cancellation_test.go b/pkg/cli/context_cancellation_test.go index 6e81480c55e..988b325121d 100644 --- a/pkg/cli/context_cancellation_test.go +++ b/pkg/cli/context_cancellation_test.go @@ -110,19 +110,26 @@ func TestRunWorkflowsOnGitHubCancellationDuringExecution(t *testing.T) { // TestDownloadWorkflowLogsTimeoutRespected tests that timeout-minutes is respected func TestDownloadWorkflowLogsTimeoutRespected(t *testing.T) { - // Use a short timeout in minutes and verify fast-fail behavior still returns quickly - ctx := context.Background() + originalFetch := logsFetchWorkflowRunBatch + t.Cleanup(func() { + logsFetchWorkflowRunBatch = originalFetch + }) + logsFetchWorkflowRunBatch = func(ctx context.Context, _ LogsDownloadOptions, _ string, _ int, _ bool) (workflowRunBatch, error) { + <-ctx.Done() + return workflowRunBatch{}, ctx.Err() + } start := time.Now() - // Use a workflow name that doesn't exist to avoid actual network calls - _ = DownloadWorkflowLogs(ctx, LogsDownloadOptions{ - WorkflowName: "nonexistent-workflow-12345", + err := DownloadWorkflowLogs(context.Background(), LogsDownloadOptions{ + WorkflowName: "test-workflow", Count: 100, - OutputDir: "/tmp/test-logs", + OutputDir: t.TempDir(), TimeoutMinutes: 1, + TimeoutSeconds: 1, }) elapsed := time.Since(start) - // Should complete within reasonable time (give 5 seconds buffer for test overhead) - assert.Less(t, elapsed, 5*time.Second, "Should complete quickly when workflow doesn't exist") + assert.NoError(t, err) + assert.GreaterOrEqual(t, elapsed, time.Second, "Should wait for the configured timeout") + assert.Less(t, elapsed, 3*time.Second, "Should stop promptly after the configured timeout") } From 1896231223d1ba187442ffe5dd36c3f19049f903 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 25 Aug 2026 03:06:19 +0200 Subject: [PATCH 3/7] Address local test suite review feedback --- .../js/generate_usage_activity_summary.cjs | 42 ++++++++-- actions/setup/js/package-lock.json | 79 ------------------- actions/setup/js/package.json | 1 - scripts/agent-report-progress.sh | 40 +--------- 4 files changed, 36 insertions(+), 126 deletions(-) diff --git a/actions/setup/js/generate_usage_activity_summary.cjs b/actions/setup/js/generate_usage_activity_summary.cjs index 663e540f9c8..e03ceb69c4b 100644 --- a/actions/setup/js/generate_usage_activity_summary.cjs +++ b/actions/setup/js/generate_usage_activity_summary.cjs @@ -11,7 +11,6 @@ // working_set: cumulative input-token traffic relative to peak invocation input const fs = require("fs"); -const { globSync } = require("glob"); const path = require("path"); const { readExperimentAssignments } = require("./experiment_helpers.cjs"); const { calculateWorkingSetFromJSONL } = require("./working_set_metrics.cjs"); @@ -29,6 +28,33 @@ const PLACEHOLDER_DEST_KEY = "-:-"; const ERROR_DOMAIN_PREFIX = "error:"; const AGENT_TOKEN_USAGE_PATH = "/tmp/gh-aw/usage/agent/token_usage.jsonl"; +function findLogFiles(rootDir) { + if (!fs.existsSync(rootDir)) { + return []; + } + + const files = []; + for (const entry of fs.readdirSync(rootDir, { withFileTypes: true })) { + const entryPath = path.join(rootDir, entry.name); + if (entry.isDirectory()) { + files.push(...findLogFiles(entryPath)); + } else if (entry.isFile() && entry.name.endsWith(".log")) { + files.push(entryPath); + } + } + return files; +} + +function findPrefixedDirectories(parentDir, prefix) { + if (!fs.existsSync(parentDir)) { + return []; + } + return fs + .readdirSync(parentDir, { withFileTypes: true }) + .filter(entry => entry.isDirectory() && entry.name.startsWith(prefix)) + .map(entry => path.join(parentDir, entry.name)); +} + /** * @param {string} [tokenUsagePath] * @returns {{ workingSet: ReturnType["workingSet"], ignoredRecords: number }} @@ -112,13 +138,15 @@ function parseFirewallLogs() { requests_by_domain: {}, }; - // The sandbox firewall logs may be emitted in nested directories (for example, - // api-proxy-logs/*.log), so these patterns are intentionally recursive. - const firewallPaths = ["/tmp/gh-aw/sandbox/firewall/logs/**/*.log", "/tmp/gh-aw/threat-detection/sandbox/firewall/logs/**/*.log", "/tmp/gh-aw/squid-logs-*/**/*.log", "/tmp/gh-aw/threat-detection/squid-logs-*/**/*.log"]; + const firewallLogDirs = [ + "/tmp/gh-aw/sandbox/firewall/logs", + "/tmp/gh-aw/threat-detection/sandbox/firewall/logs", + ...findPrefixedDirectories("/tmp/gh-aw", "squid-logs-"), + ...findPrefixedDirectories("/tmp/gh-aw/threat-detection", "squid-logs-"), + ]; - for (const pattern of firewallPaths) { - const files = globSync(pattern); - for (const logPath of files) { + for (const logDir of firewallLogDirs) { + for (const logPath of findLogFiles(logDir)) { try { const content = fs.readFileSync(logPath, "utf-8"); const lines = content.split("\n"); diff --git a/actions/setup/js/package-lock.json b/actions/setup/js/package-lock.json index aac5a009983..dc9cad88da9 100644 --- a/actions/setup/js/package-lock.json +++ b/actions/setup/js/package-lock.json @@ -16,7 +16,6 @@ "@types/node": "^26.2.0", "@vitest/coverage-v8": "^4.1.8", "@vitest/ui": "^4.1.10", - "glob": "^11.0.3", "minimatch": ">=10.2.6", "prettier": "^3.9.6", "typescript": "^7.0.2", @@ -754,16 +753,6 @@ "copilot-win32-x64": "copilot.exe" } }, - "node_modules/@isaacs/cliui": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", - "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -2934,31 +2923,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/glob": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", - "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "foreground-child": "^3.3.1", - "jackspeak": "^4.1.1", - "minimatch": "^10.1.1", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -3115,22 +3079,6 @@ "node": ">=8" } }, - "node_modules/jackspeak": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", - "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^9.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/js-tokens": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", @@ -3497,16 +3445,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lru-cache": { - "version": "11.5.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", - "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -3684,23 +3622,6 @@ "node": ">=8" } }, - "node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", diff --git a/actions/setup/js/package.json b/actions/setup/js/package.json index 0dab06cba80..f6283f6f46b 100644 --- a/actions/setup/js/package.json +++ b/actions/setup/js/package.json @@ -11,7 +11,6 @@ "@types/node": "^26.2.0", "@vitest/coverage-v8": "^4.1.8", "@vitest/ui": "^4.1.10", - "glob": "^11.0.3", "minimatch": ">=10.2.6", "prettier": "^3.9.6", "typescript": "^7.0.2", diff --git a/scripts/agent-report-progress.sh b/scripts/agent-report-progress.sh index 1ed2a1cc78a..77c94b0c2cb 100755 --- a/scripts/agent-report-progress.sh +++ b/scripts/agent-report-progress.sh @@ -168,45 +168,7 @@ lint_go_packages() { } lint_custom_go_packages() { - local output - local status - local diagnostic_found=0 - local relevant_diagnostic_found=0 - local line - local diagnostic_file - local changed_file - - set +e - output=$(make --no-print-directory golint-custom LINTER_PACKAGES="${go_packages[*]}" 2>&1) - status=$? - set -e - - if [ "$status" -eq 0 ]; then - printf '%s\n' "$output" - return 0 - fi - - while IFS= read -r line; do - if [[ "$line" =~ ^([^:]+\.go):[0-9]+:[0-9]+: ]]; then - diagnostic_found=1 - diagnostic_file="${BASH_REMATCH[1]}" - for changed_file in "${go_files[@]}"; do - if [ "$diagnostic_file" = "$changed_file" ]; then - printf '%s\n' "$line" - relevant_diagnostic_found=1 - break - fi - done - else - printf '%s\n' "$line" - fi - done <<< "$output" - - if [ "$relevant_diagnostic_found" -eq 1 ] || [ "$diagnostic_found" -eq 0 ]; then - return "$status" - fi - - echo "Custom Go linter diagnostics were limited to unchanged files; skipping them for this change-scoped gate." + make --no-print-directory golint-custom LINTER_PACKAGES="${go_packages[*]}" } lint_javascript() { From 915a972ff4359d83344af9cba1c88894036a7839 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Aug 2026 01:37:26 +0000 Subject: [PATCH 4/7] Address PR follow-up review feedback Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- actions/setup/js/awf_reflect.cjs | 12 ++- actions/setup/js/awf_reflect.test.cjs | 9 ++ .../js/generate_usage_activity_summary.cjs | 51 +++++++++-- .../setup/js/notify_comment_error.test.cjs | 5 +- pkg/cli/context_cancellation_test.go | 3 +- scripts/agent-report-progress.sh | 90 ++++++++++++++++++- 6 files changed, 156 insertions(+), 14 deletions(-) diff --git a/actions/setup/js/awf_reflect.cjs b/actions/setup/js/awf_reflect.cjs index 15e7a712cf0..0151a931469 100644 --- a/actions/setup/js/awf_reflect.cjs +++ b/actions/setup/js/awf_reflect.cjs @@ -32,7 +32,7 @@ const AWF_API_PROXY_REFLECT_URL = "http://api-proxy:10000/reflect"; // Persist outside the read-only gh-aw infrastructure mount. const AWF_REFLECT_OUTPUT_PATH = path.join(process.env.RUNNER_TEMP || os.tmpdir(), "awf-reflect.json"); // Milliseconds to wait for the /reflect endpoint before giving up. -const AWF_REFLECT_TIMEOUT_MS = Number.parseInt(process.env.GH_AW_REFLECT_TIMEOUT_MS || "60000", 10); +const AWF_REFLECT_TIMEOUT_MS = parseReflectTimeoutMs(process.env.GH_AW_REFLECT_TIMEOUT_MS); // Milliseconds to wait for each models_url fallback fetch (shorter than the main reflect timeout). const AWF_MODELS_URL_TIMEOUT_MS = 3000; // Milliseconds to wait for an api-proxy provider listener to accept a real TCP connection. @@ -84,6 +84,15 @@ const REFLECT_PROVIDER_ALIASES = { anthropic: new Set(["anthropic"]), }; +function parseReflectTimeoutMs(value) { + const rawValue = String(value || "").trim(); + if (!/^\d+$/.test(rawValue)) { + return 60000; + } + const timeoutMs = Number(rawValue); + return Number.isSafeInteger(timeoutMs) ? timeoutMs : 60000; +} + const DEFAULT_API_PROXY_HOST_BRIDGE = "host.docker.internal"; /** @@ -1027,6 +1036,7 @@ if (typeof module !== "undefined" && module.exports) { AWF_PROVIDER_LISTENER_READY_PROBE_TIMEOUT_MS, DEFAULT_API_PROXY_HOST_BRIDGE, GEMINI_MODEL_NAME_PREFIX, + parseReflectTimeoutMs, enrichReflectModels, extractModelIds, fetchAWFReflect, diff --git a/actions/setup/js/awf_reflect.test.cjs b/actions/setup/js/awf_reflect.test.cjs index 29c36c0af79..244f30f02a6 100644 --- a/actions/setup/js/awf_reflect.test.cjs +++ b/actions/setup/js/awf_reflect.test.cjs @@ -28,6 +28,7 @@ const { hasAPIProxyLocalhostAlias, inferProviderTypeForModel, inferWireApiForModel, + parseReflectTimeoutMs, resolveOpenAICompatibleEndpointFromReflect, resolveProviderEndpointFromReflect, resolveMultiProviderFromReflect, @@ -50,6 +51,14 @@ describe("awf_reflect.cjs", () => { expect(DEFAULT_API_PROXY_HOST_BRIDGE).toBe("host.docker.internal"); expect(GEMINI_MODEL_NAME_PREFIX).toBe("models/"); }); + + it("falls back to the default reflect timeout when the environment value is invalid", () => { + expect(parseReflectTimeoutMs("")).toBe(60000); + expect(parseReflectTimeoutMs("not-a-number")).toBe(60000); + expect(parseReflectTimeoutMs("12abc")).toBe(60000); + expect(parseReflectTimeoutMs("999999999999999999999999")).toBe(60000); + expect(parseReflectTimeoutMs("1234")).toBe(1234); + }); }); describe("waitForProviderListenerReady", () => { diff --git a/actions/setup/js/generate_usage_activity_summary.cjs b/actions/setup/js/generate_usage_activity_summary.cjs index e03ceb69c4b..66535ea0b74 100644 --- a/actions/setup/js/generate_usage_activity_summary.cjs +++ b/actions/setup/js/generate_usage_activity_summary.cjs @@ -34,7 +34,14 @@ function findLogFiles(rootDir) { } const files = []; - for (const entry of fs.readdirSync(rootDir, { withFileTypes: true })) { + let entries; + try { + entries = fs.readdirSync(rootDir, { withFileTypes: true }); + } catch { + return []; + } + + for (const entry of entries) { const entryPath = path.join(rootDir, entry.name); if (entry.isDirectory()) { files.push(...findLogFiles(entryPath)); @@ -45,14 +52,41 @@ function findLogFiles(rootDir) { return files; } +function findSessionEventFiles(rootDir) { + if (!fs.existsSync(rootDir)) { + return []; + } + + const files = []; + let entries; + try { + entries = fs.readdirSync(rootDir, { withFileTypes: true }); + } catch { + return []; + } + + for (const entry of entries) { + const entryPath = path.join(rootDir, entry.name); + if (entry.isDirectory()) { + files.push(...findSessionEventFiles(entryPath)); + } else if (entry.isFile() && entry.name === "events.jsonl") { + files.push(entryPath); + } + } + return files; +} + function findPrefixedDirectories(parentDir, prefix) { if (!fs.existsSync(parentDir)) { return []; } - return fs - .readdirSync(parentDir, { withFileTypes: true }) - .filter(entry => entry.isDirectory() && entry.name.startsWith(prefix)) - .map(entry => path.join(parentDir, entry.name)); + let entries; + try { + entries = fs.readdirSync(parentDir, { withFileTypes: true }); + } catch { + return []; + } + return entries.filter(entry => entry.isDirectory() && entry.name.startsWith(prefix)).map(entry => path.join(parentDir, entry.name)); } /** @@ -263,11 +297,10 @@ function parseSessionLogs() { failed_tool_executions: 0, }; - const sessionPaths = ["/tmp/gh-aw/sandbox/agent/logs/copilot-session-state/*/events.jsonl", "/tmp/gh-aw/threat-detection/sandbox/agent/logs/copilot-session-state/*/events.jsonl"]; + const sessionLogDirs = ["/tmp/gh-aw/sandbox/agent/logs/copilot-session-state", "/tmp/gh-aw/threat-detection/sandbox/agent/logs/copilot-session-state"]; - for (const pattern of sessionPaths) { - const files = globSync(pattern); - for (const eventsPath of files) { + for (const logDir of sessionLogDirs) { + for (const eventsPath of findSessionEventFiles(logDir)) { try { const content = fs.readFileSync(eventsPath, "utf-8"); const lines = content.split("\n"); diff --git a/actions/setup/js/notify_comment_error.test.cjs b/actions/setup/js/notify_comment_error.test.cjs index b0720afd1ce..4f2db5df845 100644 --- a/actions/setup/js/notify_comment_error.test.cjs +++ b/actions/setup/js/notify_comment_error.test.cjs @@ -1,6 +1,9 @@ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; import fs from "fs"; import path from "path"; +import { syncRuntimePromptTemplates } from "./test_prompt_templates.js"; + +const { runtimePromptsDir } = syncRuntimePromptTemplates(import.meta.url); const { ERR_VALIDATION } = require("./error_codes.cjs"); const mockCore = { debug: vi.fn(), @@ -62,7 +65,7 @@ const mockCore = { GH_AW_OUTPUT_ADD_COMMENT_COMMENT_URL: process.env.GH_AW_OUTPUT_ADD_COMMENT_COMMENT_URL, GH_AW_OUTPUT_CREATE_PULL_REQUEST_PULL_REQUEST_URL: process.env.GH_AW_OUTPUT_CREATE_PULL_REQUEST_PULL_REQUEST_URL, }), - (process.env.GH_AW_PROMPTS_DIR = path.join(process.cwd(), "../md"))); + (process.env.GH_AW_PROMPTS_DIR = runtimePromptsDir)); const scriptPath = path.join(process.cwd(), "notify_comment_error.cjs"); notifyCommentScript = fs.readFileSync(scriptPath, "utf8"); }), diff --git a/pkg/cli/context_cancellation_test.go b/pkg/cli/context_cancellation_test.go index 988b325121d..176bf5e114a 100644 --- a/pkg/cli/context_cancellation_test.go +++ b/pkg/cli/context_cancellation_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // TestRunWorkflowOnGitHubWithCancellation tests that RunWorkflowOnGitHub respects context cancellation @@ -129,7 +130,7 @@ func TestDownloadWorkflowLogsTimeoutRespected(t *testing.T) { }) elapsed := time.Since(start) - assert.NoError(t, err) + require.NoError(t, err) assert.GreaterOrEqual(t, elapsed, time.Second, "Should wait for the configured timeout") assert.Less(t, elapsed, 3*time.Second, "Should stop promptly after the configured timeout") } diff --git a/scripts/agent-report-progress.sh b/scripts/agent-report-progress.sh index 77c94b0c2cb..ef39a7ca555 100755 --- a/scripts/agent-report-progress.sh +++ b/scripts/agent-report-progress.sh @@ -157,10 +157,96 @@ make --no-print-directory build mapfile -t go_packages < <(printf '%s\n' "${go_packages[@]}" | sed '/^$/d' | LC_ALL=C sort -u) +normalize_repo_path() { + local file="$1" + file="${file#"$PWD"/}" + file="${file#./}" + printf '%s\n' "$file" +} + +is_changed_go_file() { + local diagnostic_file + local changed_file + + diagnostic_file=$(normalize_repo_path "$1") + for changed_file in "${go_files[@]}"; do + if [ "$diagnostic_file" = "$(normalize_repo_path "$changed_file")" ]; then + return 0 + fi + done + return 1 +} + +is_linter_summary_line() { + local line="$1" + [[ "$line" =~ ^[0-9]+[[:space:]]issues?:$ ]] || + [[ "$line" =~ ^\*[[:space:]][^:]+:[[:space:]][0-9]+$ ]] || + [[ "$line" = "Building custom linters..." ]] || + [[ "$line" = "Running custom linters (largefunc max-lines=60)..." ]] || + [[ "$line" =~ ^make\[[0-9]+\]:[[:space:]]\*\*\*[[:space:]]\[Makefile:[0-9]+:[[:space:]]golint-custom\][[:space:]]Error[[:space:]][0-9]+$ ]] +} + +run_change_scoped_go_linter() { + local label="$1" + shift + local output + local status + local diagnostic_found=0 + local relevant_diagnostic_found=0 + local non_diagnostic_failure=0 + local last_diagnostic_relevant=0 + local diagnostic_detail_lines_remaining=0 + local line + + set +e + output=$("$@" 2>&1) + status=$? + set -e + + if [ "$status" -eq 0 ]; then + printf '%s\n' "$output" + return 0 + fi + + while IFS= read -r line; do + if [[ "$line" =~ ^([^:]+\.go):[0-9]+:[0-9]+: ]]; then + diagnostic_found=1 + if is_changed_go_file "${BASH_REMATCH[1]}"; then + printf '%s\n' "$line" + relevant_diagnostic_found=1 + last_diagnostic_relevant=1 + else + last_diagnostic_relevant=0 + fi + diagnostic_detail_lines_remaining=2 + elif [ "$diagnostic_detail_lines_remaining" -gt 0 ]; then + if [ "$last_diagnostic_relevant" -eq 1 ]; then + printf '%s\n' "$line" + fi + diagnostic_detail_lines_remaining=$((diagnostic_detail_lines_remaining - 1)) + elif [[ "$line" =~ ^[[:space:]] || "$line" =~ ^[[:space:]]*\^+$ ]]; then + continue + elif is_linter_summary_line "$line"; then + continue + else + printf '%s\n' "$line" + non_diagnostic_failure=1 + last_diagnostic_relevant=0 + fi + done <<< "$output" + + if [ "$relevant_diagnostic_found" -eq 1 ] || [ "$non_diagnostic_failure" -eq 1 ] || [ "$diagnostic_found" -eq 0 ]; then + return "$status" + fi + + echo "$label diagnostics were limited to unchanged files; skipping them for this change-scoped gate." + return 0 +} + lint_go_packages() { GOPATH=$(go env GOPATH) if command -v golangci-lint >/dev/null 2>&1 || [ -x "$GOPATH/bin/golangci-lint" ]; then - PATH="$GOPATH/bin:$PATH" golangci-lint run "${go_packages[@]}" + run_change_scoped_go_linter "Go linter" env PATH="$GOPATH/bin:$PATH" golangci-lint run "${go_packages[@]}" else echo "golangci-lint is not installed. Run 'make deps-dev' to install dependencies." >&2 return 1 @@ -168,7 +254,7 @@ lint_go_packages() { } lint_custom_go_packages() { - make --no-print-directory golint-custom LINTER_PACKAGES="${go_packages[*]}" + run_change_scoped_go_linter "Custom Go linter" make --no-print-directory golint-custom LINTER_PACKAGES="${go_packages[*]}" } lint_javascript() { From 02ef698baf6cde8882da5e2dec7cbfbd126ae71c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Aug 2026 01:40:23 +0000 Subject: [PATCH 5/7] Clarify reflect timeout parser ordering Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- actions/setup/js/awf_reflect.cjs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/actions/setup/js/awf_reflect.cjs b/actions/setup/js/awf_reflect.cjs index 0151a931469..1609e97abea 100644 --- a/actions/setup/js/awf_reflect.cjs +++ b/actions/setup/js/awf_reflect.cjs @@ -25,6 +25,15 @@ const tls = require("tls"); const { withRetry, sleep } = require("./error_recovery.cjs"); const { getErrorMessage } = require("./error_helpers.cjs"); +function parseReflectTimeoutMs(value) { + const rawValue = String(value || "").trim(); + if (!/^\d+$/.test(rawValue)) { + return 60000; + } + const timeoutMs = Number(rawValue); + return Number.isSafeInteger(timeoutMs) ? timeoutMs : 60000; +} + // AWF API proxy management endpoint for discovering configured LLM providers and available models. // The api-proxy sidecar exposes /reflect on its management port (port 10000) inside the AWF // Docker network. From the agent container, the proxy is reachable via the "api-proxy" hostname. @@ -84,15 +93,6 @@ const REFLECT_PROVIDER_ALIASES = { anthropic: new Set(["anthropic"]), }; -function parseReflectTimeoutMs(value) { - const rawValue = String(value || "").trim(); - if (!/^\d+$/.test(rawValue)) { - return 60000; - } - const timeoutMs = Number(rawValue); - return Number.isSafeInteger(timeoutMs) ? timeoutMs : 60000; -} - const DEFAULT_API_PROXY_HOST_BRIDGE = "host.docker.internal"; /** From e6a736a696a718b557450bfbe372cf903940471d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Aug 2026 01:43:15 +0000 Subject: [PATCH 6/7] Deduplicate usage log discovery Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- .../js/generate_usage_activity_summary.cjs | 34 +++---------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/actions/setup/js/generate_usage_activity_summary.cjs b/actions/setup/js/generate_usage_activity_summary.cjs index 66535ea0b74..7c3279e0a7b 100644 --- a/actions/setup/js/generate_usage_activity_summary.cjs +++ b/actions/setup/js/generate_usage_activity_summary.cjs @@ -28,7 +28,7 @@ const PLACEHOLDER_DEST_KEY = "-:-"; const ERROR_DOMAIN_PREFIX = "error:"; const AGENT_TOKEN_USAGE_PATH = "/tmp/gh-aw/usage/agent/token_usage.jsonl"; -function findLogFiles(rootDir) { +function findFiles(rootDir, shouldIncludeFile) { if (!fs.existsSync(rootDir)) { return []; } @@ -44,32 +44,8 @@ function findLogFiles(rootDir) { for (const entry of entries) { const entryPath = path.join(rootDir, entry.name); if (entry.isDirectory()) { - files.push(...findLogFiles(entryPath)); - } else if (entry.isFile() && entry.name.endsWith(".log")) { - files.push(entryPath); - } - } - return files; -} - -function findSessionEventFiles(rootDir) { - if (!fs.existsSync(rootDir)) { - return []; - } - - const files = []; - let entries; - try { - entries = fs.readdirSync(rootDir, { withFileTypes: true }); - } catch { - return []; - } - - for (const entry of entries) { - const entryPath = path.join(rootDir, entry.name); - if (entry.isDirectory()) { - files.push(...findSessionEventFiles(entryPath)); - } else if (entry.isFile() && entry.name === "events.jsonl") { + files.push(...findFiles(entryPath, shouldIncludeFile)); + } else if (entry.isFile() && shouldIncludeFile(entry)) { files.push(entryPath); } } @@ -180,7 +156,7 @@ function parseFirewallLogs() { ]; for (const logDir of firewallLogDirs) { - for (const logPath of findLogFiles(logDir)) { + for (const logPath of findFiles(logDir, entry => entry.name.endsWith(".log"))) { try { const content = fs.readFileSync(logPath, "utf-8"); const lines = content.split("\n"); @@ -300,7 +276,7 @@ function parseSessionLogs() { const sessionLogDirs = ["/tmp/gh-aw/sandbox/agent/logs/copilot-session-state", "/tmp/gh-aw/threat-detection/sandbox/agent/logs/copilot-session-state"]; for (const logDir of sessionLogDirs) { - for (const eventsPath of findSessionEventFiles(logDir)) { + for (const eventsPath of findFiles(logDir, entry => entry.name === "events.jsonl")) { try { const content = fs.readFileSync(eventsPath, "utf-8"); const lines = content.split("\n"); From cb35ab76c65c8ec7548270b6b32d7668577fc059 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 25 Aug 2026 01:46:17 +0000 Subject: [PATCH 7/7] Preserve session event log depth Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- .../js/generate_usage_activity_summary.cjs | 12 +++++----- .../generate_usage_activity_summary.test.cjs | 22 ++++++++++++++++++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/actions/setup/js/generate_usage_activity_summary.cjs b/actions/setup/js/generate_usage_activity_summary.cjs index 7c3279e0a7b..9b0b801b795 100644 --- a/actions/setup/js/generate_usage_activity_summary.cjs +++ b/actions/setup/js/generate_usage_activity_summary.cjs @@ -28,7 +28,7 @@ const PLACEHOLDER_DEST_KEY = "-:-"; const ERROR_DOMAIN_PREFIX = "error:"; const AGENT_TOKEN_USAGE_PATH = "/tmp/gh-aw/usage/agent/token_usage.jsonl"; -function findFiles(rootDir, shouldIncludeFile) { +function findFiles(rootDir, shouldIncludeFile, maxDepth = Number.POSITIVE_INFINITY, currentDepth = 0) { if (!fs.existsSync(rootDir)) { return []; } @@ -44,7 +44,9 @@ function findFiles(rootDir, shouldIncludeFile) { for (const entry of entries) { const entryPath = path.join(rootDir, entry.name); if (entry.isDirectory()) { - files.push(...findFiles(entryPath, shouldIncludeFile)); + if (currentDepth < maxDepth) { + files.push(...findFiles(entryPath, shouldIncludeFile, maxDepth, currentDepth + 1)); + } } else if (entry.isFile() && shouldIncludeFile(entry)) { files.push(entryPath); } @@ -260,7 +262,7 @@ function parseFirewallLogs() { /** * Parse Copilot session event logs and aggregate counters */ -function parseSessionLogs() { +function parseSessionLogs(sessionLogDirs = ["/tmp/gh-aw/sandbox/agent/logs/copilot-session-state", "/tmp/gh-aw/threat-detection/sandbox/agent/logs/copilot-session-state"]) { const session = { total_events: 0, session_starts: 0, @@ -273,10 +275,8 @@ function parseSessionLogs() { failed_tool_executions: 0, }; - const sessionLogDirs = ["/tmp/gh-aw/sandbox/agent/logs/copilot-session-state", "/tmp/gh-aw/threat-detection/sandbox/agent/logs/copilot-session-state"]; - for (const logDir of sessionLogDirs) { - for (const eventsPath of findFiles(logDir, entry => entry.name === "events.jsonl")) { + for (const eventsPath of findFiles(logDir, entry => entry.name === "events.jsonl", 1)) { try { const content = fs.readFileSync(eventsPath, "utf-8"); const lines = content.split("\n"); diff --git a/actions/setup/js/generate_usage_activity_summary.test.cjs b/actions/setup/js/generate_usage_activity_summary.test.cjs index 6d6fac8c0a9..91508573a61 100644 --- a/actions/setup/js/generate_usage_activity_summary.test.cjs +++ b/actions/setup/js/generate_usage_activity_summary.test.cjs @@ -9,7 +9,7 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const req = createRequire(import.meta.url); -const { parseFirewallLogs, parseSafeOutputsManifest, parseExperimentsData, calculateWorkingSetFromJSONL, parseWorkingSetMetrics, MANIFEST_FILE_PATH } = req("./generate_usage_activity_summary.cjs"); +const { parseFirewallLogs, parseSessionLogs, parseSafeOutputsManifest, parseExperimentsData, calculateWorkingSetFromJSONL, parseWorkingSetMetrics, MANIFEST_FILE_PATH } = req("./generate_usage_activity_summary.cjs"); describe("generate_usage_activity_summary.cjs", () => { /** Unique directory for each test to avoid cross-test interference */ @@ -96,6 +96,26 @@ describe("generate_usage_activity_summary.cjs", () => { }); }); + describe("parseSessionLogs", () => { + it("matches events.jsonl one directory below the session-state directory", () => { + const sessionRoot = fs.mkdtempSync(path.join(os.tmpdir(), "session-logs-test-")); + try { + fs.mkdirSync(path.join(sessionRoot, "session-1"), { recursive: true }); + fs.writeFileSync(path.join(sessionRoot, "session-1", "events.jsonl"), `${JSON.stringify({ type: "session.start" })}\n`); + fs.mkdirSync(path.join(sessionRoot, "nested", "too-deep"), { recursive: true }); + fs.writeFileSync(path.join(sessionRoot, "nested", "too-deep", "events.jsonl"), `${JSON.stringify({ type: "assistant.message" })}\n`); + + expect(parseSessionLogs([sessionRoot])).toMatchObject({ + total_events: 1, + session_starts: 1, + assistant_messages: 0, + }); + } finally { + fs.rmSync(sessionRoot, { recursive: true, force: true }); + } + }); + }); + describe("parseSafeOutputsManifest", () => { /** Unique manifest file path per test to avoid cross-test interference */ let manifestPath;