diff --git a/packages/server/src/server/agent/providers/codex-app-server-agent.features.test.ts b/packages/server/src/server/agent/providers/codex-app-server-agent.features.test.ts index c86e9b7d8..781de7c65 100644 --- a/packages/server/src/server/agent/providers/codex-app-server-agent.features.test.ts +++ b/packages/server/src/server/agent/providers/codex-app-server-agent.features.test.ts @@ -5,7 +5,16 @@ import { __codexAppServerInternals } from "./codex-app-server-agent.js"; import { createTestLogger } from "../../../test-utils/test-logger.js"; const CODEX_PROVIDER = "codex"; -const TEST_COLLABORATION_MODES = [ + +interface CollaborationModeRecord { + name: string; + mode?: string | null; + model?: string | null; + reasoning_effort?: string | null; + developer_instructions?: string | null; +} + +const TEST_COLLABORATION_MODES: CollaborationModeRecord[] = [ { name: "Code", mode: "code", @@ -18,6 +27,40 @@ const TEST_COLLABORATION_MODES = [ }, ]; +interface CodexRequestFn { + (method: string, params?: unknown, timeoutMs?: number): Promise; +} + +interface CodexClientLike { + request: CodexRequestFn; +} + +interface CodexSessionTestAccess { + client: CodexClientLike | null; + connected: boolean; + currentThreadId: string | null; + serviceTier: "fast" | null; + planModeEnabled: boolean; + cachedRuntimeInfo: unknown; + ensureThreadLoaded: () => Promise; + ensureThread: () => Promise; + buildUserInput: (...args: unknown[]) => Promise; + resolveSlashCommandInvocation: (...args: unknown[]) => Promise; + collaborationModes: CollaborationModeRecord[]; + refreshResolvedCollaborationMode(): void; +} + +type CodexFeaturesTestSession = AgentSession & { + connected: boolean; + currentThreadId: string | null; + collaborationModes: CollaborationModeRecord[]; + refreshResolvedCollaborationMode(): void; +}; + +function asInternals(session: CodexFeaturesTestSession): CodexSessionTestAccess { + return session as unknown as CodexSessionTestAccess; +} + function createConfig(overrides: Partial = {}): AgentSessionConfig { return { provider: CODEX_PROVIDER, @@ -28,7 +71,9 @@ function createConfig(overrides: Partial = {}): AgentSession }; } -function createSession(configOverrides: Partial = {}) { +function createSession( + configOverrides: Partial = {}, +): CodexFeaturesTestSession { const config = createConfig(configOverrides); const session = new __codexAppServerInternals.CodexAppServerAgentSession( { ...config, provider: CODEX_PROVIDER }, @@ -37,7 +82,7 @@ function createSession(configOverrides: Partial = {}) { () => { throw new Error("Test session cannot spawn Codex app-server"); }, - ) as unknown as AgentSession & { [key: string]: unknown }; + ) as unknown as CodexFeaturesTestSession; session.connected = true; session.currentThreadId = "test-thread"; session.collaborationModes = TEST_COLLABORATION_MODES; @@ -116,7 +161,7 @@ describe("Codex app-server provider features", () => { await session.setFeature?.("fast_mode", true); - expect((session as any).serviceTier).toBe("fast"); + expect(asInternals(session).serviceTier).toBe("fast"); }); test("setFeature('fast_mode', false) clears serviceTier to null", async () => { @@ -126,18 +171,18 @@ describe("Codex app-server provider features", () => { await session.setFeature?.("fast_mode", false); - expect((session as any).serviceTier).toBeNull(); + expect(asInternals(session).serviceTier).toBeNull(); }); test("setFeature invalidates cachedRuntimeInfo", async () => { const session = createSession(); await session.getRuntimeInfo(); - expect((session as any).cachedRuntimeInfo).not.toBeNull(); + expect(asInternals(session).cachedRuntimeInfo).not.toBeNull(); await session.setFeature?.("fast_mode", true); - expect((session as any).cachedRuntimeInfo).toBeNull(); + expect(asInternals(session).cachedRuntimeInfo).toBeNull(); }); test("setFeature throws for unknown feature ids", async () => { @@ -153,8 +198,8 @@ describe("Codex app-server provider features", () => { featureValues: { fast_mode: true, plan_mode: true }, }); - expect((session as any).serviceTier).toBe("fast"); - expect((session as any).planModeEnabled).toBe(true); + expect(asInternals(session).serviceTier).toBe("fast"); + expect(asInternals(session).planModeEnabled).toBe(true); expect(session.features).toEqual([ { type: "toggle", @@ -180,13 +225,13 @@ describe("Codex app-server provider features", () => { test("startTurn includes serviceTier when fast mode is enabled", async () => { const session = createSession(); const request = vi.fn().mockResolvedValue(undefined); - (session as any).client = { request }; - (session as any).connected = true; - (session as any).currentThreadId = "thread-123"; - (session as any).ensureThreadLoaded = vi.fn().mockResolvedValue(undefined); - (session as any).ensureThread = vi.fn().mockResolvedValue(undefined); - (session as any).buildUserInput = vi.fn().mockResolvedValue([{ type: "text", text: "hi" }]); - (session as any).resolveSlashCommandInvocation = vi.fn().mockResolvedValue(null); + asInternals(session).client = { request }; + asInternals(session).connected = true; + asInternals(session).currentThreadId = "thread-123"; + asInternals(session).ensureThreadLoaded = vi.fn().mockResolvedValue(undefined); + asInternals(session).ensureThread = vi.fn().mockResolvedValue(undefined); + asInternals(session).buildUserInput = vi.fn().mockResolvedValue([{ type: "text", text: "hi" }]); + asInternals(session).resolveSlashCommandInvocation = vi.fn().mockResolvedValue(null); await session.setFeature?.("fast_mode", true); await session.startTurn("hello"); @@ -203,13 +248,13 @@ describe("Codex app-server provider features", () => { test("setModel clears fast mode when switching to an unsupported model", async () => { const session = createSession(); const request = vi.fn().mockResolvedValue(undefined); - (session as any).client = { request }; - (session as any).connected = true; - (session as any).currentThreadId = "thread-123"; - (session as any).ensureThreadLoaded = vi.fn().mockResolvedValue(undefined); - (session as any).ensureThread = vi.fn().mockResolvedValue(undefined); - (session as any).buildUserInput = vi.fn().mockResolvedValue([{ type: "text", text: "hi" }]); - (session as any).resolveSlashCommandInvocation = vi.fn().mockResolvedValue(null); + asInternals(session).client = { request }; + asInternals(session).connected = true; + asInternals(session).currentThreadId = "thread-123"; + asInternals(session).ensureThreadLoaded = vi.fn().mockResolvedValue(undefined); + asInternals(session).ensureThread = vi.fn().mockResolvedValue(undefined); + asInternals(session).buildUserInput = vi.fn().mockResolvedValue([{ type: "text", text: "hi" }]); + asInternals(session).resolveSlashCommandInvocation = vi.fn().mockResolvedValue(null); await session.setFeature?.("fast_mode", true); await session.setModel("gpt-3.5-turbo"); @@ -225,7 +270,7 @@ describe("Codex app-server provider features", () => { value: false, }, ]); - expect((session as any).serviceTier).toBeNull(); + expect(asInternals(session).serviceTier).toBeNull(); await session.startTurn("hello"); @@ -241,13 +286,13 @@ describe("Codex app-server provider features", () => { test("startTurn switches collaboration mode when plan mode is enabled", async () => { const session = createSession(); const request = vi.fn().mockResolvedValue(undefined); - (session as any).client = { request }; - (session as any).connected = true; - (session as any).currentThreadId = "thread-123"; - (session as any).ensureThreadLoaded = vi.fn().mockResolvedValue(undefined); - (session as any).ensureThread = vi.fn().mockResolvedValue(undefined); - (session as any).buildUserInput = vi.fn().mockResolvedValue([{ type: "text", text: "hi" }]); - (session as any).resolveSlashCommandInvocation = vi.fn().mockResolvedValue(null); + asInternals(session).client = { request }; + asInternals(session).connected = true; + asInternals(session).currentThreadId = "thread-123"; + asInternals(session).ensureThreadLoaded = vi.fn().mockResolvedValue(undefined); + asInternals(session).ensureThread = vi.fn().mockResolvedValue(undefined); + asInternals(session).buildUserInput = vi.fn().mockResolvedValue([{ type: "text", text: "hi" }]); + asInternals(session).resolveSlashCommandInvocation = vi.fn().mockResolvedValue(null); await session.setFeature?.("plan_mode", true); await session.startTurn("hello");