diff --git a/package-lock.json b/package-lock.json index 5a831056..5cb501a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1284,7 +1284,6 @@ "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -1931,7 +1930,6 @@ "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -2634,7 +2632,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2926,7 +2923,8 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/send": { "version": "0.19.2", @@ -3190,7 +3188,6 @@ "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" @@ -3276,7 +3273,6 @@ "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -3494,7 +3490,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts index e3622324..31b00c5d 100644 --- a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts +++ b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts @@ -3,7 +3,17 @@ import {describe, expect, it, vi, beforeEach} from 'vitest'; import type {CodexAuthRequest} from "../../CodexAuthMethod"; import type * as acp from "@agentclientprotocol/sdk"; -import {createTestFixture, createCodexMockTestFixture, createTestSessionState, type TestFixture} from "../acp-test-utils"; +import { + createTestFixture, + createCodexMockTestFixture, + createTestModel, + createTestSessionState, + createTestThread, + createTestThreadStartResponse, + createTestTurn, + createTestTurnCompletedNotification, + type TestFixture +} from "../acp-test-utils"; import type {ServerNotification} from "../../app-server"; import type {SessionState} from "../../CodexAcpServer"; import {AgentMode} from "../../AgentMode"; @@ -143,32 +153,13 @@ describe('ACP server test', { timeout: 40_000 }, () => { const codexAppServerClient = mockFixture.getCodexAppServerClient(); const listSkillsSpy = vi.spyOn(codexAppServerClient, "listSkills").mockResolvedValue({ data: [] }); - const threadStartSpy = vi.spyOn(codexAppServerClient, "threadStart").mockResolvedValue({ - thread: { id: "thread-id" } as any, - model: "gpt-5", - modelProvider: "openai", - cwd: "/workspace", - approvalPolicy: "on-request", - sandbox: "workspace-write", - reasoningEffort: "medium", - } as any); + const threadStartSpy = vi.spyOn(codexAppServerClient, "threadStart").mockResolvedValue( + createTestThreadStartResponse({ + thread: createTestThread({ id: "thread-id" }), + }) + ); vi.spyOn(codexAppServerClient, "listModels").mockResolvedValue({ - data: [{ - 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: [], - isDefault: true - }], + data: [createTestModel({ displayName: "gpt-5" })], nextCursor: null }); @@ -233,24 +224,19 @@ describe('ACP server test', { timeout: 40_000 }, () => { const codexAppServerClient = mockFixture.getCodexAppServerClient(); vi.spyOn(codexAcpAgent, "checkAuthorization").mockResolvedValue(undefined); - vi.spyOn(codexAppServerClient, "threadStart").mockResolvedValue({ - thread: { id: "thread-id" } as any, - model: "gpt-5", - reasoningEffort: "medium", - } as any); + vi.spyOn(codexAppServerClient, "threadStart").mockResolvedValue( + createTestThreadStartResponse({ + thread: createTestThread({ id: "thread-id" }), + }) + ); vi.spyOn(codexAppServerClient, "listModels").mockResolvedValue({ - data: [{ - id: "gpt-5", - name: "GPT-5", - inputModalities: ["text"], - supportedReasoningEfforts: [], - }], - hasMore: false, - } as any); + data: [createTestModel({ supportedReasoningEfforts: [] })], + nextCursor: null, + }); vi.spyOn(codexAppServerClient, "accountRead").mockResolvedValue({ requiresOpenaiAuth: false, account: null, - } as any); + }); vi.spyOn(codexAppServerClient, "listSkills").mockResolvedValue({ data: [] }); const mcpServer = { name: "broken-mcp", @@ -287,12 +273,14 @@ describe('ACP server test', { timeout: 40_000 }, () => { 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); + turn: createTestTurn() + }); + vi.spyOn(codexAppServerClient, "awaitTurnCompleted").mockResolvedValue( + createTestTurnCompletedNotification({ + threadId: "session-id", + turn: createTestTurn({ status: "completed" }) + }) + ); vi.spyOn(codexAcpAgent, "getSessionState").mockReturnValue(createTestSessionState({ sessionId: "session-id", diff --git a/src/__tests__/acp-test-utils.ts b/src/__tests__/acp-test-utils.ts index d702f0d2..7ce3b5c1 100644 --- a/src/__tests__/acp-test-utils.ts +++ b/src/__tests__/acp-test-utils.ts @@ -4,6 +4,13 @@ import {startCodexConnection} from "../CodexJsonRpcConnection"; import {CodexAcpServer, type SessionState} from "../CodexAcpServer"; import type {AgentSideConnection, RequestPermissionResponse} from "@agentclientprotocol/sdk"; import type {ServerNotification} from "../app-server"; +import type { + Model, + Thread, + ThreadStartResponse, + Turn, + TurnCompletedNotification +} from "../app-server/v2"; import type {MessageConnection} from "vscode-jsonrpc/node"; import path from "node:path"; import fs from "node:fs"; @@ -315,6 +322,96 @@ export function createTestSessionState(overrides?: Partial): Sessi }; } +export function createTestModel(overrides?: Partial): Model { + return { + 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: [], + isDefault: true, + ...overrides, + }; +} + +export function createTestTurn(overrides?: Partial): Turn { + return { + id: "turn-id", + items: [], + status: "inProgress", + error: null, + startedAt: null, + completedAt: null, + durationMs: null, + ...overrides, + }; +} + +export function createTestThread(overrides?: Partial): Thread { + return { + id: "thread-id", + forkedFromId: null, + preview: "", + ephemeral: false, + modelProvider: "openai", + createdAt: 0, + updatedAt: 0, + status: { type: "idle" }, + path: null, + cwd: "/workspace", + cliVersion: "0.0.0-test", + source: "appServer", + agentNickname: null, + agentRole: null, + gitInfo: null, + name: null, + turns: [], + ...overrides, + }; +} + +export function createTestThreadStartResponse(overrides?: Partial): ThreadStartResponse { + return { + thread: createTestThread(), + model: "gpt-5", + modelProvider: "openai", + serviceTier: null, + cwd: "/workspace", + instructionSources: [], + approvalPolicy: "on-request", + approvalsReviewer: "user", + sandbox: { + type: "workspaceWrite", + writableRoots: [], + readOnlyAccess: { type: "fullAccess" }, + networkAccess: false, + excludeTmpdirEnvVar: false, + excludeSlashTmp: false, + }, + permissionProfile: null, + reasoningEffort: "medium", + ...overrides, + }; +} + +export function createTestTurnCompletedNotification( + overrides?: Partial +): TurnCompletedNotification { + return { + threadId: "session-id", + turn: createTestTurn({ status: "completed" }), + ...overrides, + }; +} + export async function setupPromptAndSendNotifications( fixture: CodexMockTestFixture, sessionId: string, @@ -323,15 +420,17 @@ export async function setupPromptAndSendNotifications( ): Promise { const codexAcpAgent = fixture.getCodexAcpAgent(); const codexAppServerClient = fixture.getCodexAppServerClient(); - const turn = { id: "turn-id", items: [], status: "inProgress" as const, error: null }; + const turn = createTestTurn(); codexAppServerClient.turnStart = vi.fn().mockResolvedValue({ turn, }); - codexAppServerClient.awaitTurnCompleted = vi.fn().mockResolvedValue({ - threadId: sessionId, - turn: { ...turn, status: "completed" }, - }); + codexAppServerClient.awaitTurnCompleted = vi.fn().mockResolvedValue( + createTestTurnCompletedNotification({ + threadId: sessionId, + turn: createTestTurn({ ...turn, status: "completed" }), + }) + ); vi.spyOn(codexAcpAgent, "getSessionState").mockReturnValue(sessionState);