diff --git a/src/CodexAcpClient.ts b/src/CodexAcpClient.ts index 0f873e4..e9700e6 100644 --- a/src/CodexAcpClient.ts +++ b/src/CodexAcpClient.ts @@ -30,6 +30,7 @@ import type { Model, SkillsListParams, SkillsListResponse, + SandboxPolicy, Thread, ThreadSourceKind, TurnCompletedNotification, @@ -50,6 +51,7 @@ export class CodexAcpClient { private pendingLoginCompleted: Promise | null = null; private pendingAccountUpdated: Promise | null = null; private readonly sessionNotificationQueues = new Map>(); + private skillExtraRoots: string[] = []; constructor(codexClient: CodexAppServerClient, codexConfig?: JsonObject, modelProvider?: string) { @@ -202,10 +204,11 @@ export class CodexAcpClient { } async resumeSession(request: acp.ResumeSessionRequest, onSubscribed?: () => void): Promise { - await this.refreshSkills(request.cwd, request._meta); + const additionalDirectories = readAdditionalDirectories(request.cwd, request.additionalDirectories); + await this.refreshSkills(request.cwd, additionalDirectories, request._meta); const response = await this.codexClient.threadResume({ - config: await this.createSessionConfig(request.cwd, request.mcpServers ?? []), + config: await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers ?? []), cwd: request.cwd, modelProvider: this.getResumeModelProvider(), threadId: request.sessionId, @@ -218,12 +221,16 @@ export class CodexAcpClient { currentModelId: currentModelId, models: codexModels, currentServiceTier: response.serviceTier as ServiceTier ?? null, + additionalDirectories, } } async loadSession(request: acp.LoadSessionRequest, onSubscribed?: () => void): Promise { + const additionalDirectories = readAdditionalDirectories(request.cwd, request.additionalDirectories); + await this.refreshSkills(request.cwd, additionalDirectories, request._meta); + const response = await this.codexClient.threadResume({ - config: await this.createSessionConfig(request.cwd, request.mcpServers ?? []), + config: await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers ?? []), cwd: request.cwd, modelProvider: this.getResumeModelProvider(), threadId: request.sessionId, @@ -237,14 +244,16 @@ export class CodexAcpClient { models: codexModels, currentServiceTier: response.serviceTier as ServiceTier ?? null, thread: response.thread, + additionalDirectories, }; } async newSession(request: acp.NewSessionRequest): Promise { - await this.refreshSkills(request.cwd, request._meta); + const additionalDirectories = readAdditionalDirectories(request.cwd, request.additionalDirectories); + await this.refreshSkills(request.cwd, additionalDirectories, request._meta); const response = await this.codexClient.threadStart({ - config: await this.createSessionConfig(request.cwd, request.mcpServers), + config: await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers), modelProvider: this.getModelProvider(), cwd: request.cwd, }); @@ -259,6 +268,7 @@ export class CodexAcpClient { currentModelId: currentModelId, models: codexModels, currentServiceTier: response.serviceTier as ServiceTier ?? null, + additionalDirectories, }; } @@ -278,17 +288,21 @@ export class CodexAcpClient { return this.codexClient.getMcpServerStartupVersion(); } - private async createSessionConfig(projectPath: string, mcpServers: Array): Promise { + private async createSessionConfig( + projectPath: string, + additionalDirectories: string[], + mcpServers: Array + ): Promise { + const sessionRoots = [projectPath, ...additionalDirectories]; const mergedConfig = { ...mergeGatewayConfig(this.config, this.gatewayConfig), - projects: { - [projectPath]: { - trust_level: "trusted", - } - }, + projects: Object.fromEntries(sessionRoots.map(root => [root, { + trust_level: "trusted", + }])), }; + const configWithWorkspaceRoots = mergeSandboxWorkspaceWriteRoots(mergedConfig, additionalDirectories); if (mcpServers.length === 0) { - return mergedConfig; + return configWithWorkspaceRoots; } // Deduplicates new servers against existing config to prevent Codex from deep-merging @@ -300,11 +314,11 @@ export class CodexAcpClient { })); const uniqueServers = requestedServers.filter(mcp => !existingNames.has(mcp.name)); if (uniqueServers.length === 0) { - return mergedConfig; + return configWithWorkspaceRoots; } return { - ...mergedConfig, + ...configWithWorkspaceRoots, "mcp_servers": Object.fromEntries(uniqueServers.map(mcp => [mcp.name, this.createMcpSeverConfig(mcp.server)])), }; } @@ -328,17 +342,25 @@ export class CodexAcpClient { return this.getModelProvider() ?? "openai"; } - private async refreshSkills(cwd: string, meta?: Record | null): Promise { + private async refreshSkills( + cwd: string, + additionalDirectories: string[], + meta?: Record | null + ): Promise { if (!cwd) { return; } - const additionalRoots = readAdditionalRoots(meta).map(root => path.join(root, ".agents", "skills")); - if (additionalRoots.length > 0) { + const additionalRoots = uniqueStrings([ + ...readAdditionalRoots(meta), + ...additionalDirectories, + ]).map(root => path.join(root, ".agents", "skills")); + if (!arraysEqual(this.skillExtraRoots, additionalRoots)) { await this.codexClient.skillsExtraRootsSet({ extraRoots: additionalRoots }); + this.skillExtraRoots = additionalRoots; } await this.codexClient.listSkills({ - cwds: [cwd], + cwds: [cwd, ...additionalDirectories], forceReload: true, }); } @@ -444,13 +466,14 @@ export class CodexAcpClient { serviceTier: ServiceTier | null, disableSummary: boolean, cwd: string, + additionalDirectories: string[], onTurnStarted?: (turnId: string) => void, shouldCancel?: () => boolean, ): Promise { const input = buildPromptItems(request.prompt); const effort = modelId.effort as ReasoningEffort | null; //TODO remove unsafe conversion - await this.refreshSkills(cwd, request._meta); + await this.refreshSkills(cwd, additionalDirectories, request._meta); if (shouldCancel?.()) { return null; } @@ -458,7 +481,7 @@ export class CodexAcpClient { threadId: request.sessionId, input: input, approvalPolicy: agentMode.approvalPolicy, - sandboxPolicy: agentMode.sandboxPolicy, + sandboxPolicy: addAdditionalDirectoriesToSandboxPolicy(agentMode.sandboxPolicy, additionalDirectories), summary: disableSummary ? "none" : null, effort: effort, model: modelId.model, @@ -641,6 +664,7 @@ export type SessionMetadata = { currentModelId: string, models: Model[], currentServiceTier?: ServiceTier | null, + additionalDirectories: string[], } export type SessionMetadataWithThread = SessionMetadata & { @@ -701,10 +725,92 @@ function readAdditionalRoots(meta: Record | null | undefined): return []; } - return Array.from(new Set(rawRoots + return uniqueStrings(rawRoots .filter((value): value is string => typeof value === "string") .map(value => value.trim()) - .filter(value => value.length > 0))); + .filter(value => value.length > 0)); +} + +function readAdditionalDirectories(cwd: string, rawDirectories: unknown): string[] { + if (rawDirectories === undefined) { + return []; + } + if (rawDirectories === null) { + throw RequestError.invalidParams(undefined, "additionalDirectories must be an array"); + } + if (!Array.isArray(rawDirectories)) { + throw RequestError.invalidParams(undefined, "additionalDirectories must be an array"); + } + + const directories: string[] = []; + const seen = new Set([cwd]); + for (const directory of rawDirectories) { + if (typeof directory !== "string") { + throw RequestError.invalidParams(undefined, "additionalDirectories entries must be strings"); + } + if (directory.length === 0) { + throw RequestError.invalidParams(undefined, "additionalDirectories entries must not be empty"); + } + if (!path.isAbsolute(directory)) { + throw RequestError.invalidParams(undefined, "additionalDirectories entries must be absolute paths"); + } + if (!seen.has(directory)) { + seen.add(directory); + directories.push(directory); + } + } + + return directories; +} + +function mergeSandboxWorkspaceWriteRoots(config: JsonObject, roots: string[]): JsonObject { + if (roots.length === 0) { + return config; + } + + const existingSandboxConfig = isJsonObject(config["sandbox_workspace_write"]) + ? config["sandbox_workspace_write"] + : {}; + const existingWritableRoots = Array.isArray(existingSandboxConfig["writable_roots"]) + ? existingSandboxConfig["writable_roots"].filter((value): value is string => typeof value === "string") + : []; + + return { + ...config, + sandbox_workspace_write: { + ...existingSandboxConfig, + writable_roots: uniqueStrings([...existingWritableRoots, ...roots]), + }, + }; +} + +function addAdditionalDirectoriesToSandboxPolicy( + sandboxPolicy: SandboxPolicy, + additionalDirectories: string[] +): SandboxPolicy { + if (additionalDirectories.length === 0 || sandboxPolicy.type !== "workspaceWrite") { + return sandboxPolicy; + } + + return { + ...sandboxPolicy, + writableRoots: uniqueStrings([...sandboxPolicy.writableRoots, ...additionalDirectories]), + }; +} + +function uniqueStrings(values: string[]): string[] { + return Array.from(new Set(values)); +} + +function arraysEqual(left: string[], right: string[]): boolean { + if (left.length !== right.length) { + return false; + } + return left.every((value, index) => value === right[index]); +} + +function isJsonObject(value: JsonValue | undefined): value is JsonObject { + return value !== null && typeof value === "object" && !Array.isArray(value); } function mergeGatewayConfig(config: JsonObject, gatewayConfig: GatewayConfig | null): JsonObject { diff --git a/src/CodexAcpServer.ts b/src/CodexAcpServer.ts index 0003c05..1a5c24f 100644 --- a/src/CodexAcpServer.ts +++ b/src/CodexAcpServer.ts @@ -65,6 +65,7 @@ export interface SessionState { rateLimits: RateLimitsMap | null; account: Account | null; cwd: string; + additionalDirectories: string[]; fastModeEnabled: boolean; currentModelSupportsFast: boolean; sessionMcpServers?: Array; @@ -160,6 +161,7 @@ export class CodexAcpServer implements acp.Agent { resume: { }, list: { }, close: { }, + additionalDirectories: {}, }, mcpCapabilities: { acp: false, @@ -346,6 +348,7 @@ export class CodexAcpServer implements acp.Agent { rateLimits: null, account: account, cwd: request.cwd, + additionalDirectories: sessionMetadata.additionalDirectories, fastModeEnabled: sessionMetadata.currentServiceTier === "fast", currentModelSupportsFast: currentModelSupportsFast, sessionMcpServers: sessionMcpServers, @@ -418,7 +421,20 @@ export class CodexAcpServer implements acp.Agent { async listSessions(params: acp.ListSessionsRequest): Promise { logger.log("Listing sessions...", {cwd: params.cwd, cursor: params.cursor}); await this.checkAuthorization(); - return await this.runWithProcessCheck(() => this.codexAcpClient.listSessions(params)); + const response = await this.runWithProcessCheck(() => this.codexAcpClient.listSessions(params)); + return { + ...response, + sessions: response.sessions.map((session) => { + const activeSession = this.sessions.get(session.sessionId); + if (!activeSession || activeSession.additionalDirectories.length === 0) { + return session; + } + return { + ...session, + additionalDirectories: activeSession.additionalDirectories, + }; + }), + }; } async closeSession(params: acp.CloseSessionRequest): Promise { @@ -735,6 +751,7 @@ export class CodexAcpServer implements acp.Agent { rateLimits: null, account: account, cwd: request.cwd, + additionalDirectories: sessionMetadata.additionalDirectories, fastModeEnabled: sessionMetadata.currentServiceTier === "fast", currentModelSupportsFast: currentModelSupportsFast, sessionMcpServers: sessionMcpServers, @@ -1270,6 +1287,7 @@ export class CodexAcpServer implements acp.Agent { serviceTier, disableSummary, sessionState.cwd, + sessionState.additionalDirectories, (turnId) => { if (this.promptIsClosedOrStale(params.sessionId, activePrompt)) { this.interruptLateStartedTurn(params.sessionId, turnId); diff --git a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts index 355e330..8ea4ac7 100644 --- a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts +++ b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts @@ -268,6 +268,119 @@ describe('ACP server test', { timeout: 40_000 }, () => { expect(listSkillsSpy.mock.invocationCallOrder[0]!).toBeLessThan(threadStartSpy.mock.invocationCallOrder[0]!); }); + it('applies ACP additional directories to new session config and skill discovery', async () => { + const mockFixture = createCodexMockTestFixture(); + const codexAcpClient = mockFixture.getCodexAcpClient(); + const codexAppServerClient = mockFixture.getCodexAppServerClient(); + + const extraRootsSetSpy = vi.spyOn(codexAppServerClient, "skillsExtraRootsSet").mockResolvedValue(undefined); + const listSkillsSpy = vi.spyOn(codexAppServerClient, "listSkills").mockResolvedValue({data: []}); + const threadStartSpy = vi.spyOn(codexAppServerClient, "threadStart").mockResolvedValue({ + thread: {id: "thread-id"} as any, + model: "gpt-5", + reasoningEffort: "medium", + serviceTier: null, + } as any); + vi.spyOn(codexAppServerClient, "listModels").mockResolvedValue({ + data: [createTestModel({id: "gpt-5"})], + nextCursor: null, + }); + + const session = await codexAcpClient.newSession({ + cwd: "/workspace", + additionalDirectories: ["/workspace/extra", "/workspace", "/workspace/extra"], + mcpServers: [], + }); + + expect(session.additionalDirectories).toEqual(["/workspace/extra"]); + expect(extraRootsSetSpy).toHaveBeenCalledWith({ + extraRoots: ["/workspace/extra/.agents/skills"], + }); + expect(listSkillsSpy).toHaveBeenCalledWith({ + cwds: ["/workspace", "/workspace/extra"], + forceReload: true, + }); + expect(extraRootsSetSpy.mock.invocationCallOrder[0]!).toBeLessThan(threadStartSpy.mock.invocationCallOrder[0]!); + expect(listSkillsSpy.mock.invocationCallOrder[0]!).toBeLessThan(threadStartSpy.mock.invocationCallOrder[0]!); + + const threadStartRequest = threadStartSpy.mock.calls[0]![0]; + expect(threadStartRequest.config?.["projects"]).toEqual({ + "/workspace": {trust_level: "trusted"}, + "/workspace/extra": {trust_level: "trusted"}, + }); + expect(threadStartRequest.config?.["sandbox_workspace_write"]).toEqual({ + writable_roots: ["/workspace/extra"], + }); + }); + + it('applies ACP additional directories to resumed and loaded sessions explicitly', async () => { + const mockFixture = createCodexMockTestFixture(); + const codexAcpClient = mockFixture.getCodexAcpClient(); + const codexAppServerClient = mockFixture.getCodexAppServerClient(); + + vi.spyOn(codexAppServerClient, "skillsExtraRootsSet").mockResolvedValue(undefined); + vi.spyOn(codexAppServerClient, "listSkills").mockResolvedValue({data: []}); + const threadResumeSpy = vi.spyOn(codexAppServerClient, "threadResume").mockResolvedValue({ + thread: {id: "thread-id"} as any, + model: "gpt-5", + reasoningEffort: "medium", + serviceTier: null, + } as any); + vi.spyOn(codexAppServerClient, "listModels").mockResolvedValue({ + data: [createTestModel({id: "gpt-5"})], + nextCursor: null, + }); + + const resumed = await codexAcpClient.resumeSession({ + sessionId: "resume-id", + cwd: "/workspace", + additionalDirectories: ["/workspace/resume-extra"], + }); + const loaded = await codexAcpClient.loadSession({ + sessionId: "load-id", + cwd: "/workspace", + additionalDirectories: ["/workspace/load-extra"], + mcpServers: [], + }); + + expect(resumed.additionalDirectories).toEqual(["/workspace/resume-extra"]); + expect(loaded.additionalDirectories).toEqual(["/workspace/load-extra"]); + expect(threadResumeSpy.mock.calls[0]![0].config?.["projects"]).toEqual({ + "/workspace": {trust_level: "trusted"}, + "/workspace/resume-extra": {trust_level: "trusted"}, + }); + expect(threadResumeSpy.mock.calls[1]![0].config?.["projects"]).toEqual({ + "/workspace": {trust_level: "trusted"}, + "/workspace/load-extra": {trust_level: "trusted"}, + }); + }); + + it('rejects malformed ACP additional directories', async () => { + const mockFixture = createCodexMockTestFixture(); + const codexAcpClient = mockFixture.getCodexAcpClient(); + + await expect(codexAcpClient.newSession({ + cwd: "/workspace", + additionalDirectories: ["relative"], + mcpServers: [], + })).rejects.toThrow("additionalDirectories entries must be absolute paths"); + await expect(codexAcpClient.newSession({ + cwd: "/workspace", + additionalDirectories: [""], + mcpServers: [], + })).rejects.toThrow("additionalDirectories entries must not be empty"); + await expect(codexAcpClient.newSession({ + cwd: "/workspace", + additionalDirectories: [null], + mcpServers: [], + } as unknown as acp.NewSessionRequest)).rejects.toThrow("additionalDirectories entries must be strings"); + await expect(codexAcpClient.newSession({ + cwd: "/workspace", + additionalDirectories: null, + mcpServers: [], + } as unknown as acp.NewSessionRequest)).rejects.toThrow("additionalDirectories must be an array"); + }); + it('sanitizes whitespace in ACP MCP server names before adding them to Codex config', async () => { const mockFixture = createCodexMockTestFixture(); const codexAcpClient = mockFixture.getCodexAcpClient(); @@ -452,6 +565,45 @@ describe('ACP server test', { timeout: 40_000 }, () => { expect(listSkillsSpy.mock.invocationCallOrder[0]!).toBeLessThan(turnStartSpy.mock.invocationCallOrder[0]!); }); + it('applies ACP additional directories to turn skill discovery and sandbox policy', async () => { + const mockFixture = createCodexMockTestFixture(); + const codexAcpAgent = mockFixture.getCodexAcpAgent(); + const codexAppServerClient = mockFixture.getCodexAppServerClient(); + + const extraRootsSetSpy = vi.spyOn(codexAppServerClient, "skillsExtraRootsSet").mockResolvedValue(undefined); + const listSkillsSpy = vi.spyOn(codexAppServerClient, "listSkills").mockResolvedValue({data: []}); + const turnStartSpy = vi.spyOn(codexAppServerClient, "turnStart").mockResolvedValue({ + turn: { id: "turn-id", items: [], status: "inProgress", error: null } + } as any); + vi.spyOn(codexAppServerClient, "awaitTurnCompleted").mockResolvedValue({ + threadId: "session-id", + turn: { id: "turn-id", items: [], status: "completed", error: null } + } as any); + + vi.spyOn(codexAcpAgent, "getSessionState").mockReturnValue(createTestSessionState({ + sessionId: "session-id", + cwd: "/workspace", + additionalDirectories: ["/workspace/extra"], + })); + + await codexAcpAgent.prompt({ + sessionId: "session-id", + prompt: [{ type: "text", text: "Hello" }], + }); + + expect(extraRootsSetSpy).toHaveBeenCalledWith({ + extraRoots: ["/workspace/extra/.agents/skills"], + }); + expect(listSkillsSpy).toHaveBeenCalledWith({ + cwds: ["/workspace", "/workspace/extra"], + forceReload: true, + }); + expect(turnStartSpy.mock.calls[0]![0].sandboxPolicy).toMatchObject({ + type: "workspaceWrite", + writableRoots: ["/workspace/extra"], + }); + }); + function loadNotifications(){ //TODO collect logs form dev run and then load them from file to speedup const serverNotifications: ServerNotification[] = [ diff --git a/src/__tests__/CodexACPAgent/fast-mode-config.test.ts b/src/__tests__/CodexACPAgent/fast-mode-config.test.ts index 0863837..70716c8 100644 --- a/src/__tests__/CodexACPAgent/fast-mode-config.test.ts +++ b/src/__tests__/CodexACPAgent/fast-mode-config.test.ts @@ -33,6 +33,7 @@ describe("Fast mode session config", () => { currentModelId: "fast-model[medium]", models: [fastModel], currentServiceTier, + additionalDirectories: [], }); await codexAcpAgent.initialize({ diff --git a/src/__tests__/CodexACPAgent/initialize.test.ts b/src/__tests__/CodexACPAgent/initialize.test.ts index 0d6e3af..82ccecc 100644 --- a/src/__tests__/CodexACPAgent/initialize.test.ts +++ b/src/__tests__/CodexACPAgent/initialize.test.ts @@ -50,6 +50,7 @@ describe('CodexACPAgent - initialize', () => { resume: {}, list: {}, close: {}, + additionalDirectories: {}, }, mcpCapabilities: { acp: false, diff --git a/src/__tests__/CodexACPAgent/list-sessions.test.ts b/src/__tests__/CodexACPAgent/list-sessions.test.ts index c5d4805..0c30283 100644 --- a/src/__tests__/CodexACPAgent/list-sessions.test.ts +++ b/src/__tests__/CodexACPAgent/list-sessions.test.ts @@ -137,4 +137,81 @@ describe("CodexACPAgent - list sessions", () => { "data/list-sessions-thread-name.json" ); }); + + it("includes tracked additional directories for active sessions", async () => { + const fixture = createCodexMockTestFixture(); + const codexAcpAgent = fixture.getCodexAcpAgent(); + const codexAcpClient = fixture.getCodexAcpClient(); + const codexAppServerClient = fixture.getCodexAppServerClient(); + + vi.spyOn(codexAcpClient, "authRequired").mockResolvedValue(false); + vi.spyOn(codexAcpClient, "getAccount").mockResolvedValue({ + account: null, + requiresOpenaiAuth: false, + }); + vi.spyOn(codexAcpClient, "newSession").mockResolvedValue({ + sessionId: "sess-1", + currentModelId: "gpt-5[medium]", + models: [{ + id: "gpt-5", + model: "gpt-5", + upgrade: null, + upgradeInfo: null, + availabilityNux: null, + displayName: "gpt-5", + description: "test model", + hidden: false, + supportedReasoningEfforts: [{reasoningEffort: "medium", description: "balanced"}], + defaultReasoningEffort: "medium", + inputModalities: ["text"], + supportsPersonality: false, + additionalSpeedTiers: [], + serviceTiers: [], + defaultServiceTier: null, + isDefault: true, + }], + currentServiceTier: null, + additionalDirectories: ["/repo/extra"], + }); + const thread: Thread = { + id: "sess-1", + sessionId: "sess-1", + parentThreadId: null, + threadSource: null, + forkedFromId: null, + preview: "First session", + ephemeral: false, + modelProvider: "openai", + createdAt: 100, + updatedAt: 200, + status: { type: "idle" }, + path: null, + cwd: "/repo/project", + cliVersion: "0.0.0", + source: "cli", + agentNickname: null, + agentRole: null, + gitInfo: null, + name: null, + turns: [], + }; + vi.spyOn(codexAppServerClient, "threadList").mockResolvedValue({ + data: [thread], + nextCursor: null, + backwardsCursor: null, + }); + + await codexAcpAgent.newSession({ + cwd: "/repo/project", + additionalDirectories: ["/repo/extra"], + mcpServers: [], + }); + + const response = await codexAcpAgent.listSessions({ + cwd: null, + cursor: null, + }); + + expect(response.sessions[0]?.additionalDirectories).toEqual(["/repo/extra"]); + }); }); diff --git a/src/__tests__/CodexACPAgent/model-filtering.test.ts b/src/__tests__/CodexACPAgent/model-filtering.test.ts index d72c419..280410a 100644 --- a/src/__tests__/CodexACPAgent/model-filtering.test.ts +++ b/src/__tests__/CodexACPAgent/model-filtering.test.ts @@ -128,6 +128,7 @@ describe("Model filtering", () => { sessionId: "session-id", currentModelId: "gpt-5.2[medium]", models, + additionalDirectories: [], }); vi.spyOn(codexAcpClient, "getAccount").mockResolvedValue({account: null, requiresOpenaiAuth: false}); diff --git a/src/__tests__/CodexACPAgent/session-close.test.ts b/src/__tests__/CodexACPAgent/session-close.test.ts index 60520f5..032abe3 100644 --- a/src/__tests__/CodexACPAgent/session-close.test.ts +++ b/src/__tests__/CodexACPAgent/session-close.test.ts @@ -458,6 +458,7 @@ async function createSession(options: { currentModelId: "model-id[medium]", models: [model], currentServiceTier: null, + additionalDirectories: [], }); options.configure?.({fixture, codexAcpAgent, codexAcpClient}); @@ -503,6 +504,7 @@ function createSessionMetadata(): SessionMetadata { currentModelId: "model-id[medium]", models: [createTestModel()], currentServiceTier: null, + additionalDirectories: [], }; } diff --git a/src/__tests__/CodexACPAgent/session-config-options.test.ts b/src/__tests__/CodexACPAgent/session-config-options.test.ts index e01c9b3..abd8da2 100644 --- a/src/__tests__/CodexACPAgent/session-config-options.test.ts +++ b/src/__tests__/CodexACPAgent/session-config-options.test.ts @@ -40,6 +40,7 @@ async function createSession(currentModelId: string, availableModels: Array { sessionId: "session-id", currentModelId: "fast-model[medium]", models: [fast], + additionalDirectories: [], }); await codexAcpAgent.newSession({cwd: "/test/cwd", mcpServers: []}); diff --git a/src/__tests__/acp-test-utils.ts b/src/__tests__/acp-test-utils.ts index 32b7b8e..24edf15 100644 --- a/src/__tests__/acp-test-utils.ts +++ b/src/__tests__/acp-test-utils.ts @@ -338,6 +338,7 @@ export function createTestSessionState(overrides?: Partial): Sessi rateLimits: null, account: null, cwd: "/test/cwd", + additionalDirectories: [], sessionId: "session-id", currentModelId: "model-id[effort]", availableModels: [],