diff --git a/actions/setup/js/copilot_sdk_driver.test.cjs b/actions/setup/js/copilot_sdk_driver.test.cjs index fd88ddcecd1..ab2dc541bfa 100644 --- a/actions/setup/js/copilot_sdk_driver.test.cjs +++ b/actions/setup/js/copilot_sdk_driver.test.cjs @@ -1,10 +1,26 @@ -import { describe, it, expect, vi } from "vitest"; +import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; import { createRequire } from "module"; +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; const require = createRequire(import.meta.url); const { runWithCopilotSDK, parsePermissionConfigFromServerArgs } = require("./copilot_sdk_driver.cjs"); describe("copilot_sdk_driver.cjs", () => { + let testSessionStateDir; + let prevSessionStateDir; + beforeAll(() => { + prevSessionStateDir = process.env.GH_AW_SESSION_STATE_BASE_DIR; + testSessionStateDir = fs.mkdtempSync(path.join(os.tmpdir(), "gh-aw-test-session-state-")); + process.env.GH_AW_SESSION_STATE_BASE_DIR = testSessionStateDir; + }); + afterAll(() => { + if (prevSessionStateDir === undefined) delete process.env.GH_AW_SESSION_STATE_BASE_DIR; + else process.env.GH_AW_SESSION_STATE_BASE_DIR = prevSessionStateDir; + if (testSessionStateDir) fs.rmSync(testSessionStateDir, { recursive: true, force: true }); + }); + describe("runWithCopilotSDK", () => { it("disconnects session and stops client on success", async () => { const disconnect = vi.fn().mockResolvedValue(undefined); diff --git a/actions/setup/js/copilot_sdk_session.cjs b/actions/setup/js/copilot_sdk_session.cjs index c8510abb884..82b04c323cc 100644 --- a/actions/setup/js/copilot_sdk_session.cjs +++ b/actions/setup/js/copilot_sdk_session.cjs @@ -82,10 +82,11 @@ function extractPromptFromArgs(args) { * RuntimeConnection: typeof import("@github/copilot-sdk").RuntimeConnection, * approveAll: typeof import("@github/copilot-sdk").approveAll * }, + * sessionStateBaseDir?: string, * }} options * @returns {Promise<{exitCode: number, output: string, hasOutput: boolean, durationMs: number}>} */ -async function runWithCopilotSDK({ sdkUri, prompt, logger, attempt = 0, model, connectionToken, provider, maxToolDenials, permissionConfig, coreLogger, sdkModule }) { +async function runWithCopilotSDK({ sdkUri, prompt, logger, attempt = 0, model, connectionToken, provider, maxToolDenials, permissionConfig, coreLogger, sdkModule, sessionStateBaseDir }) { // Lazy-require to avoid loading the SDK when it is not needed. // The SDK is large and has side-effects on import (worker threads, etc.). const { CopilotClient, RuntimeConnection, approveAll } = sdkModule ?? require("@github/copilot-sdk"); @@ -106,7 +107,9 @@ async function runWithCopilotSDK({ sdkUri, prompt, logger, attempt = 0, model, c // Session state directory — mirrors the target path used by unified_timeline.cjs. // /tmp/gh-aw/sandbox/agent/logs/copilot-session-state/{sessionId}/events.jsonl - const sessionStateBase = path.join(os.tmpdir(), "gh-aw", "sandbox", "agent", "logs", "copilot-session-state"); + // GH_AW_SESSION_STATE_BASE_DIR may be set in tests to redirect writes to an isolated directory. + const defaultSessionStateBase = path.join(os.tmpdir(), "gh-aw", "sandbox", "agent", "logs", "copilot-session-state"); + const sessionStateBase = sessionStateBaseDir ?? process.env.GH_AW_SESSION_STATE_BASE_DIR ?? defaultSessionStateBase; /** @type {ReadonlyArray>} */ const VALID_LOG_LEVELS = ["none", "error", "warning", "info", "debug", "all"];