From f5ba66bfee6a064cfd2689b02d5c4e118cc9ecb7 Mon Sep 17 00:00:00 2001 From: Peter Kirkham Date: Mon, 12 Jan 2026 16:42:45 -0800 Subject: [PATCH 1/2] feat: remove codex --- apps/array/src/main/services/agent/schemas.ts | 6 - apps/array/src/main/services/agent/service.ts | 4 - .../sessions/components/FrameworkSelector.tsx | 74 -- .../features/sessions/stores/sessionStore.ts | 9 +- .../features/settings/stores/settingsStore.ts | 10 +- .../components/TaskInputEditor.tsx | 16 +- apps/array/src/shared/types/models.ts | 24 +- apps/array/src/types/analytics.ts | 2 - packages/agent/package.json | 1 - packages/agent/src/adapters/claude/claude.ts | 1 - packages/agent/src/adapters/codex/codex.ts | 788 ------------------ packages/agent/src/adapters/connection.ts | 20 +- packages/agent/src/types.ts | 2 +- pnpm-lock.yaml | 9 - 14 files changed, 17 insertions(+), 949 deletions(-) delete mode 100644 apps/array/src/renderer/features/sessions/components/FrameworkSelector.tsx delete mode 100644 packages/agent/src/adapters/codex/codex.ts diff --git a/apps/array/src/main/services/agent/schemas.ts b/apps/array/src/main/services/agent/schemas.ts index 0e5d29209..d9083aecc 100644 --- a/apps/array/src/main/services/agent/schemas.ts +++ b/apps/array/src/main/services/agent/schemas.ts @@ -9,10 +9,6 @@ export const credentialsSchema = z.object({ export type Credentials = z.infer; -// Agent framework schema -export const agentFrameworkSchema = z.enum(["claude", "codex"]); -export type AgentFramework = z.infer; - // Execution mode schema export const executionModeSchema = z.enum(["plan", "acceptEdits", "default"]); export type ExecutionMode = z.infer; @@ -26,7 +22,6 @@ export const sessionConfigSchema = z.object({ logUrl: z.string().optional(), sdkSessionId: z.string().optional(), model: z.string().optional(), - framework: agentFrameworkSchema.optional(), executionMode: executionModeSchema.optional(), }); @@ -44,7 +39,6 @@ export const startSessionInput = z.object({ permissionMode: z.string().optional(), autoProgress: z.boolean().optional(), model: z.string().optional(), - framework: agentFrameworkSchema.optional().default("claude"), executionMode: z.enum(["plan", "acceptEdits", "default"]).optional(), runMode: z.enum(["local", "cloud"]).optional(), createPR: z.boolean().optional(), diff --git a/apps/array/src/main/services/agent/service.ts b/apps/array/src/main/services/agent/service.ts index 3f020da1a..8390b3cd8 100644 --- a/apps/array/src/main/services/agent/service.ts +++ b/apps/array/src/main/services/agent/service.ts @@ -129,7 +129,6 @@ interface SessionConfig { logUrl?: string; sdkSessionId?: string; model?: string; - framework?: "claude" | "codex"; executionMode?: "plan" | "acceptEdits" | "default"; } @@ -318,7 +317,6 @@ export class AgentService extends TypedEventEmitter { logUrl, sdkSessionId, model, - framework, executionMode, } = config; @@ -345,7 +343,6 @@ export class AgentService extends TypedEventEmitter { try { const { clientStreams } = await agent.runTaskV2(taskId, taskRunId, { skipGitBranch: true, - framework, isReconnect, }); @@ -790,7 +787,6 @@ export class AgentService extends TypedEventEmitter { logUrl: "logUrl" in params ? params.logUrl : undefined, sdkSessionId: "sdkSessionId" in params ? params.sdkSessionId : undefined, model: "model" in params ? params.model : undefined, - framework: "framework" in params ? params.framework : "claude", executionMode: "executionMode" in params ? params.executionMode : undefined, }; diff --git a/apps/array/src/renderer/features/sessions/components/FrameworkSelector.tsx b/apps/array/src/renderer/features/sessions/components/FrameworkSelector.tsx deleted file mode 100644 index cd92ee4f5..000000000 --- a/apps/array/src/renderer/features/sessions/components/FrameworkSelector.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { useSettingsStore } from "@features/settings/stores/settingsStore"; -import { Select, Text } from "@radix-ui/themes"; -import { - type AgentFramework, - AVAILABLE_FRAMEWORKS, -} from "@shared/types/models"; -import { useSessionForTask } from "../stores/sessionStore"; - -interface FrameworkSelectorProps { - taskId?: string; - disabled?: boolean; - onFrameworkChange?: (framework: AgentFramework) => void; -} - -export function FrameworkSelector({ - taskId, - disabled, - onFrameworkChange, -}: FrameworkSelectorProps) { - const defaultFramework = useSettingsStore((state) => state.defaultFramework); - const setDefaultFramework = useSettingsStore( - (state) => state.setDefaultFramework, - ); - const session = useSessionForTask(taskId); - - // Use session framework if available, otherwise fall back to default - const activeFramework = session?.framework ?? defaultFramework; - - // Disable if there's an active session (can't change framework mid-session) - const isDisabled = disabled || session?.status === "connected"; - - const handleChange = (value: string) => { - const framework = value as AgentFramework; - setDefaultFramework(framework); - onFrameworkChange?.(framework); - }; - - const currentFramework = AVAILABLE_FRAMEWORKS.find( - (f) => f.id === activeFramework, - ); - const displayName = currentFramework?.name ?? activeFramework; - - return ( - - - - {displayName} - - - - {AVAILABLE_FRAMEWORKS.filter((f) => f.enabled).map((framework) => ( - - {framework.name} - - ))} - - - ); -} diff --git a/apps/array/src/renderer/features/sessions/stores/sessionStore.ts b/apps/array/src/renderer/features/sessions/stores/sessionStore.ts index 77ed992bd..21be3a530 100644 --- a/apps/array/src/renderer/features/sessions/stores/sessionStore.ts +++ b/apps/array/src/renderer/features/sessions/stores/sessionStore.ts @@ -50,7 +50,7 @@ export interface AgentSession { logUrl?: string; processedLineCount?: number; model?: string; - framework?: "claude" | "codex"; + framework?: "claude"; // Current execution mode (plan = read-only, default = manual approve, acceptEdits = auto-approve edits) currentMode: ExecutionMode; // Permission requests waiting for user response @@ -414,7 +414,6 @@ function createBaseSession( taskRunId: string, taskId: string, isCloud: boolean, - framework?: "claude" | "codex", executionMode?: "plan" | "acceptEdits", ): AgentSession { return { @@ -426,7 +425,6 @@ function createBaseSession( status: "connecting", isPromptPending: false, isCloud, - framework, currentMode: executionMode ?? "default", pendingPermissions: new Map(), }; @@ -640,7 +638,7 @@ const useStore = create()( return; } - const { defaultModel, defaultFramework } = useSettingsStore.getState(); + const { defaultModel } = useSettingsStore.getState(); const result = await trpcVanilla.agent.start.mutate({ taskId, taskRunId: taskRun.id, @@ -649,7 +647,6 @@ const useStore = create()( apiHost: auth.apiHost, projectId: auth.projectId, model: defaultModel, - framework: defaultFramework, executionMode, }); @@ -657,7 +654,6 @@ const useStore = create()( taskRun.id, taskId, false, - defaultFramework, executionMode, ); session.channel = result.channel; @@ -671,7 +667,6 @@ const useStore = create()( task_id: taskId, execution_type: "local", model: defaultModel, - framework: defaultFramework, }); if (initialPrompt?.length) { diff --git a/apps/array/src/renderer/features/settings/stores/settingsStore.ts b/apps/array/src/renderer/features/settings/stores/settingsStore.ts index 70bc47b7c..05b5a07af 100644 --- a/apps/array/src/renderer/features/settings/stores/settingsStore.ts +++ b/apps/array/src/renderer/features/settings/stores/settingsStore.ts @@ -1,9 +1,5 @@ import type { WorkspaceMode } from "@shared/types"; -import { - type AgentFramework, - DEFAULT_FRAMEWORK, - DEFAULT_MODEL, -} from "@shared/types/models"; +import { DEFAULT_MODEL } from "@shared/types/models"; import { create } from "zustand"; import { persist } from "zustand/middleware"; @@ -18,7 +14,6 @@ interface SettingsStore { lastUsedWorkspaceMode: WorkspaceMode; createPR: boolean; defaultModel: string; - defaultFramework: AgentFramework; desktopNotifications: boolean; setAutoRunTasks: (autoRun: boolean) => void; @@ -28,7 +23,6 @@ interface SettingsStore { setLastUsedWorkspaceMode: (mode: WorkspaceMode) => void; setCreatePR: (createPR: boolean) => void; setDefaultModel: (model: string) => void; - setDefaultFramework: (framework: AgentFramework) => void; setDesktopNotifications: (enabled: boolean) => void; } @@ -42,7 +36,6 @@ export const useSettingsStore = create()( lastUsedWorkspaceMode: "worktree", createPR: true, defaultModel: DEFAULT_MODEL, - defaultFramework: DEFAULT_FRAMEWORK, desktopNotifications: true, setAutoRunTasks: (autoRun) => set({ autoRunTasks: autoRun }), @@ -53,7 +46,6 @@ export const useSettingsStore = create()( setLastUsedWorkspaceMode: (mode) => set({ lastUsedWorkspaceMode: mode }), setCreatePR: (createPR) => set({ createPR }), setDefaultModel: (model) => set({ defaultModel: model }), - setDefaultFramework: (framework) => set({ defaultFramework: framework }), setDesktopNotifications: (enabled) => set({ desktopNotifications: enabled }), }), diff --git a/apps/array/src/renderer/features/task-detail/components/TaskInputEditor.tsx b/apps/array/src/renderer/features/task-detail/components/TaskInputEditor.tsx index 3157b08e6..fe864c256 100644 --- a/apps/array/src/renderer/features/task-detail/components/TaskInputEditor.tsx +++ b/apps/array/src/renderer/features/task-detail/components/TaskInputEditor.tsx @@ -3,7 +3,6 @@ import { EditorToolbar } from "@features/message-editor/components/EditorToolbar import type { MessageEditorHandle } from "@features/message-editor/components/MessageEditor"; import { ModeIndicatorInput } from "@features/message-editor/components/ModeIndicatorInput"; import { useTiptapEditor } from "@features/message-editor/tiptap/useTiptapEditor"; -import { FrameworkSelector } from "@features/sessions/components/FrameworkSelector"; import type { ExecutionMode } from "@features/sessions/stores/sessionStore"; import { ArrowUp, GitBranchIcon } from "@phosphor-icons/react"; import { Box, Flex, IconButton, Text, Tooltip } from "@radix-ui/themes"; @@ -195,15 +194,12 @@ export const TaskInputEditor = forwardRef< - - - - + {!isCloudMode && ( diff --git a/apps/array/src/shared/types/models.ts b/apps/array/src/shared/types/models.ts index ceefc7c6e..7c1a74d52 100644 --- a/apps/array/src/shared/types/models.ts +++ b/apps/array/src/shared/types/models.ts @@ -66,29 +66,7 @@ export const AVAILABLE_MODELS: ModelOption[] = [ export const DEFAULT_MODEL = "claude-opus-4-5"; // Agent frameworks -export type AgentFramework = "claude" | "codex"; - -export interface FrameworkOption { - id: AgentFramework; - name: string; - description: string; - enabled: boolean; -} - -export const AVAILABLE_FRAMEWORKS: FrameworkOption[] = [ - { - id: "claude", - name: "Claude Code", - description: "Anthropic's Claude Code agent", - enabled: true, - }, - { - id: "codex", - name: "OpenAI Codex", - description: "OpenAI's Codex agent", - enabled: true, - }, -]; +export type AgentFramework = "claude"; export const DEFAULT_FRAMEWORK: AgentFramework = "claude"; diff --git a/apps/array/src/types/analytics.ts b/apps/array/src/types/analytics.ts index 26196b358..be99ffb52 100644 --- a/apps/array/src/types/analytics.ts +++ b/apps/array/src/types/analytics.ts @@ -14,7 +14,6 @@ export type GitActionType = | "create-pr"; export type FileOpenSource = "sidebar" | "agent-suggestion" | "search" | "diff"; export type FileChangeType = "added" | "modified" | "deleted"; -export type AgentFramework = "claude" | "codex"; export type StopReason = "user_cancelled" | "completed" | "error" | "timeout"; export type CommandMenuAction = | "home" @@ -62,7 +61,6 @@ export interface TaskRunStartedProperties { task_id: string; execution_type: ExecutionType; model?: string; - framework?: AgentFramework; } export interface TaskRunCompletedProperties { diff --git a/packages/agent/package.json b/packages/agent/package.json index ea9776ae3..d380472d2 100644 --- a/packages/agent/package.json +++ b/packages/agent/package.json @@ -45,7 +45,6 @@ "dependencies": { "@agentclientprotocol/sdk": "^0.5.1", "@anthropic-ai/claude-agent-sdk": "^0.1.55", - "@openai/codex-sdk": "0.60.1", "@anthropic-ai/sdk": "^0.71.0", "@modelcontextprotocol/sdk": "^1.23.0", "diff": "^8.0.2", diff --git a/packages/agent/src/adapters/claude/claude.ts b/packages/agent/src/adapters/claude/claude.ts index 5caebea63..191d9f0c3 100644 --- a/packages/agent/src/adapters/claude/claude.ts +++ b/packages/agent/src/adapters/claude/claude.ts @@ -1943,6 +1943,5 @@ export function streamEventToAcpNotifications( } // Note: createAcpConnection has been moved to ../connection.ts -// to support multiple agent frameworks (Claude, Codex). // Import from there instead: // import { createAcpConnection } from "../connection.js"; diff --git a/packages/agent/src/adapters/codex/codex.ts b/packages/agent/src/adapters/codex/codex.ts deleted file mode 100644 index 965afa7bc..000000000 --- a/packages/agent/src/adapters/codex/codex.ts +++ /dev/null @@ -1,788 +0,0 @@ -/** - * Codex ACP Agent - * - * Wraps the OpenAI Codex SDK to implement the ACP Agent interface, - * allowing Codex to be used as an alternative agent framework. - */ - -import { execSync } from "node:child_process"; -import { existsSync } from "node:fs"; -import { - type Agent, - type AgentSideConnection, - type AuthenticateRequest, - type CancelNotification, - type ClientCapabilities, - type InitializeRequest, - type InitializeResponse, - type LoadSessionRequest, - type LoadSessionResponse, - type NewSessionRequest, - type NewSessionResponse, - type PromptRequest, - type PromptResponse, - RequestError, - type SessionModelState, - type SessionNotification, - type SetSessionModelRequest, - type SetSessionModeRequest, - type SetSessionModeResponse, -} from "@agentclientprotocol/sdk"; -import { - Codex, - type CodexOptions, - type CommandExecutionItem, - type FileChangeItem, - type McpToolCallItem, - type Thread, - type ThreadEvent, - type ThreadItem, - type ThreadOptions, - type WebSearchItem, -} from "@openai/codex-sdk"; -import { v7 as uuidv7 } from "uuid"; -import type { - SessionPersistenceConfig, - SessionStore, -} from "@/session-store.js"; -import { Logger } from "@/utils/logger.js"; -import packageJson from "../../../package.json" with { type: "json" }; - -/** - * Find the codex CLI binary path. - * Checks common locations and falls back to PATH lookup. - */ -function findCodexCliPath(): string | undefined { - // Common installation paths - const commonPaths = [ - "/opt/homebrew/bin/codex", // macOS Apple Silicon - "/usr/local/bin/codex", // macOS Intel / Linux - "/usr/bin/codex", // Linux system - ]; - - for (const path of commonPaths) { - if (existsSync(path)) { - return path; - } - } - - // Try to find via which command - try { - const whichResult = execSync("which codex", { - encoding: "utf-8", - timeout: 5000, - }).trim(); - if (whichResult && existsSync(whichResult)) { - return whichResult; - } - } catch { - // which command failed, codex not in PATH - } - - return undefined; -} - -type CodexSession = { - thread: Thread; - threadId?: string; - cancelled: boolean; - notificationHistory: SessionNotification[]; -}; - -export class CodexAcpAgent implements Agent { - private codex: Codex; - private sessions: Map; - private client: AgentSideConnection; - private clientCapabilities?: ClientCapabilities; - private logger: Logger; - private sessionStore?: SessionStore; - - constructor(client: AgentSideConnection, sessionStore?: SessionStore) { - const codexPath = findCodexCliPath(); - const codexOptions: CodexOptions = {}; - - if (codexPath) { - codexOptions.codexPathOverride = codexPath; - } - - let gatewayUrl = process.env.OPENAI_BASE_URL; - - if (!gatewayUrl && process.env.LLM_GATEWAY_URL) { - const baseUrl = process.env.LLM_GATEWAY_URL; - gatewayUrl = baseUrl.endsWith("/v1") ? baseUrl : `${baseUrl}/v1`; - } - - const apiKey = - process.env.OPENAI_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN; - - if (gatewayUrl || apiKey) { - const env: Record = { - PATH: process.env.PATH || "", - HOME: process.env.HOME || "", - }; - - if (gatewayUrl) { - env.OPENAI_BASE_URL = gatewayUrl; - } - if (apiKey) { - env.OPENAI_API_KEY = apiKey; - } - - codexOptions.env = env; - } - - this.codex = new Codex(codexOptions); - this.sessions = new Map(); - this.client = client; - this.sessionStore = sessionStore; - this.logger = new Logger({ debug: true, prefix: "[CodexAcpAgent]" }); - - if (codexPath) { - this.logger.info("Using Codex CLI", { - path: codexPath, - gatewayUrl: gatewayUrl || "not set", - }); - } else { - this.logger.warn( - "Codex CLI not found. Install with: npm install -g @openai/codex", - ); - } - } - - async initialize(request: InitializeRequest): Promise { - this.clientCapabilities = request.clientCapabilities; - - return { - protocolVersion: 1, - agentCapabilities: { - promptCapabilities: { - image: true, - embeddedContext: false, - }, - mcpCapabilities: { - http: false, - sse: false, - }, - loadSession: true, - _meta: { - posthog: { - resumeSession: true, - }, - }, - }, - agentInfo: { - name: packageJson.name, - title: "OpenAI Codex", - version: packageJson.version, - }, - authMethods: [], - }; - } - - async newSession(params: NewSessionRequest): Promise { - const sessionId = - (params._meta as { sessionId?: string } | undefined)?.sessionId || - uuidv7(); - - const threadOptions: ThreadOptions = { - workingDirectory: params.cwd, - sandboxMode: "danger-full-access", - skipGitRepoCheck: true, - approvalPolicy: "never", - }; - - this.logger.info("Starting Codex thread", { - cwd: params.cwd, - options: threadOptions, - }); - - const thread = this.codex.startThread(threadOptions); - - const session: CodexSession = { - thread, - cancelled: false, - notificationHistory: [], - }; - - this.sessions.set(sessionId, session); - - const persistence = params._meta?.persistence as - | SessionPersistenceConfig - | undefined; - if (persistence && this.sessionStore) { - this.sessionStore.register(sessionId, persistence); - this.logger.info("Registered session for S3 persistence", { - sessionId, - taskId: persistence.taskId, - runId: persistence.runId, - }); - } - - this.logger.info("Created new Codex session", { sessionId }); - - const models: SessionModelState = { - availableModels: [ - { - modelId: "codex", - name: "OpenAI Codex", - description: "OpenAI Codex agent", - }, - ], - currentModelId: "codex", - }; - - const availableModes = [ - { - id: "default", - name: "Default", - description: "Standard approval mode", - }, - ]; - - return { - sessionId, - models, - modes: { - currentModeId: "default", - availableModes, - }, - }; - } - - async authenticate(_params: AuthenticateRequest): Promise { - throw new Error("Authentication not implemented for Codex"); - } - - async prompt(params: PromptRequest): Promise { - const session = this.sessions.get(params.sessionId); - if (!session) { - throw new Error("Session not found"); - } - - session.cancelled = false; - - const promptText = params.prompt - .filter((p) => p.type === "text") - .map((p) => (p as { type: "text"; text: string }).text) - .join("\n"); - - this.logger.info("Running Codex prompt", { - sessionId: params.sessionId, - promptLength: promptText.length, - promptPreview: promptText.substring(0, 100), - }); - - for (const chunk of params.prompt) { - const userNotification: SessionNotification = { - sessionId: params.sessionId, - update: { - sessionUpdate: "user_message_chunk", - content: chunk, - }, - }; - await this.client.sessionUpdate(userNotification); - this.appendNotification(params.sessionId, userNotification); - } - - try { - const streamedTurn = await session.thread.runStreamed(promptText); - - for await (const event of streamedTurn.events) { - if (session.cancelled) { - return { stopReason: "cancelled" }; - } - - const notifications = this.convertEventToNotifications( - event, - params.sessionId, - ); - - for (const notification of notifications) { - await this.client.sessionUpdate(notification); - this.appendNotification(params.sessionId, notification); - } - - if (event.type === "thread.started" && "id" in event) { - session.threadId = event.id as string; - this.client.extNotification("_posthog/sdk_session", { - sessionId: params.sessionId, - sdkSessionId: event.id as string, - }); - } - } - - return { stopReason: "end_turn" }; - } catch (error) { - const errorMessage = - error instanceof Error ? error.message : String(error); - this.logger.error("Codex prompt failed", { - error: errorMessage, - stack: error instanceof Error ? error.stack : undefined, - }); - throw RequestError.internalError(undefined, errorMessage); - } - } - - async cancel(params: CancelNotification): Promise { - const session = this.sessions.get(params.sessionId); - if (!session) { - throw new Error("Session not found"); - } - session.cancelled = true; - this.logger.info("Cancelled Codex session", { - sessionId: params.sessionId, - }); - } - - async setSessionModel(_params: SetSessionModelRequest): Promise { - // No-op: Codex model is fixed - } - - async setSessionMode( - params: SetSessionModeRequest, - ): Promise { - if (params.modeId !== "default") { - throw new Error(`Mode ${params.modeId} not supported by Codex`); - } - return {}; - } - - async loadSession(params: LoadSessionRequest): Promise { - return this.resumeSession(params); - } - - /** - * Resume a session without replaying history. - * Client is responsible for fetching and rendering history from S3. - */ - async resumeSession( - params: LoadSessionRequest, - ): Promise { - const { sessionId } = params; - - const persistence = params._meta?.persistence as - | SessionPersistenceConfig - | undefined; - - const existingSession = this.sessions.get(sessionId); - const threadId = existingSession?.threadId; - - if (threadId) { - const threadOptions: ThreadOptions = { - workingDirectory: params.cwd, - }; - - const thread = this.codex.resumeThread(threadId, threadOptions); - - const session: CodexSession = { - thread, - threadId, - cancelled: false, - notificationHistory: existingSession?.notificationHistory || [], - }; - - this.sessions.set(sessionId, session); - this.logger.info("Resumed Codex thread", { sessionId, threadId }); - - if (persistence && this.sessionStore) { - this.sessionStore.register(sessionId, persistence); - this.logger.info("Registered resumed session for S3 persistence", { - sessionId, - taskId: persistence.taskId, - runId: persistence.runId, - }); - } - } else { - this.logger.info("No thread ID found, creating new session", { - sessionId, - }); - await this.newSession({ - ...params, - _meta: { ...(params._meta || {}), sessionId, persistence }, - } as NewSessionRequest); - } - - return {}; - } - - /** - * Handle custom extension methods. - * Per ACP spec, extension methods start with underscore. - */ - async extMethod( - method: string, - params: Record, - ): Promise> { - if (method === "_posthog/session/resume") { - await this.resumeSession(params as unknown as LoadSessionRequest); - return {}; - } - - throw RequestError.methodNotFound(method); - } - - private appendNotification( - sessionId: string, - notification: SessionNotification, - ): void { - const session = this.sessions.get(sessionId); - if (session) { - session.notificationHistory.push(notification); - } - } - - private convertEventToNotifications( - event: ThreadEvent, - sessionId: string, - ): SessionNotification[] { - const notifications: SessionNotification[] = []; - - switch (event.type) { - case "thread.started": - case "turn.started": - case "turn.completed": - break; - - case "item.started": - if ("item" in event && event.item) { - const item = event.item as ThreadItem; - notifications.push( - ...this.itemStartedToNotifications(item, sessionId), - ); - } - break; - - case "item.updated": - if ("item" in event && event.item) { - const item = event.item as ThreadItem; - notifications.push( - ...this.itemUpdatedToNotifications(item, sessionId), - ); - } - break; - - case "item.completed": - if ("item" in event && event.item) { - const item = event.item as ThreadItem; - notifications.push( - ...this.itemCompletedToNotifications(item, sessionId), - ); - } - break; - - case "turn.failed": - if ("error" in event) { - const error = event.error as { message?: string } | undefined; - notifications.push({ - sessionId, - update: { - sessionUpdate: "agent_message_chunk", - content: { - type: "text", - text: `Turn failed: ${error?.message || "Unknown error"}`, - }, - }, - }); - } - break; - - case "error": - if ("message" in event) { - notifications.push({ - sessionId, - update: { - sessionUpdate: "agent_message_chunk", - content: { - type: "text", - text: `Error: ${event.message || "Unknown error"}`, - }, - }, - }); - } - break; - } - - return notifications; - } - - private itemStartedToNotifications( - item: ThreadItem, - sessionId: string, - ): SessionNotification[] { - const notifications: SessionNotification[] = []; - - switch (item.type) { - case "command_execution": { - const cmdItem = item as CommandExecutionItem; - notifications.push({ - sessionId, - update: { - sessionUpdate: "tool_call", - toolCallId: cmdItem.id, - title: `Bash: ${this.truncateCommand(cmdItem.command)}`, - status: "in_progress", - rawInput: { command: cmdItem.command }, - _meta: { - codex: { - toolName: "Bash", - command: cmdItem.command, - }, - }, - }, - }); - break; - } - - case "file_change": { - const fileItem = item as FileChangeItem; - const paths = fileItem.changes.map((c) => c.path).join(", "); - notifications.push({ - sessionId, - update: { - sessionUpdate: "tool_call", - toolCallId: fileItem.id, - title: `File changes: ${paths}`, - status: "in_progress", - rawInput: { changes: fileItem.changes }, - _meta: { - codex: { - toolName: "FileChange", - changes: fileItem.changes, - }, - }, - }, - }); - break; - } - - case "mcp_tool_call": { - const mcpItem = item as McpToolCallItem; - notifications.push({ - sessionId, - update: { - sessionUpdate: "tool_call", - toolCallId: mcpItem.id, - title: `MCP: ${mcpItem.server}/${mcpItem.tool}`, - status: "in_progress", - rawInput: - mcpItem.arguments && typeof mcpItem.arguments === "object" - ? (mcpItem.arguments as Record) - : undefined, - _meta: { - codex: { - toolName: "McpToolCall", - server: mcpItem.server, - tool: mcpItem.tool, - }, - }, - }, - }); - break; - } - - case "web_search": { - const searchItem = item as WebSearchItem; - notifications.push({ - sessionId, - update: { - sessionUpdate: "tool_call", - toolCallId: searchItem.id, - title: `Web Search: ${searchItem.query}`, - status: "in_progress", - rawInput: { query: searchItem.query }, - _meta: { - codex: { - toolName: "WebSearch", - query: searchItem.query, - }, - }, - }, - }); - break; - } - } - - return notifications; - } - - private itemUpdatedToNotifications( - item: ThreadItem, - sessionId: string, - ): SessionNotification[] { - const notifications: SessionNotification[] = []; - - if (item.type === "command_execution") { - const cmdItem = item as CommandExecutionItem; - if (cmdItem.aggregated_output) { - notifications.push({ - sessionId, - update: { - sessionUpdate: "tool_call_update", - toolCallId: cmdItem.id, - status: "in_progress", - _meta: { - codex: { - toolName: "Bash", - output: cmdItem.aggregated_output, - }, - }, - }, - }); - } - } - - return notifications; - } - - private itemCompletedToNotifications( - item: ThreadItem, - sessionId: string, - ): SessionNotification[] { - const notifications: SessionNotification[] = []; - - switch (item.type) { - case "agent_message": { - const text = (item as { text?: string }).text; - if (text) { - notifications.push({ - sessionId, - update: { - sessionUpdate: "agent_message_chunk", - content: { - type: "text", - text, - }, - }, - }); - } - break; - } - - case "reasoning": { - const text = (item as { text?: string }).text; - if (text) { - notifications.push({ - sessionId, - update: { - sessionUpdate: "agent_thought_chunk", - content: { - type: "text", - text, - }, - }, - }); - } - break; - } - - case "command_execution": { - const cmdItem = item as CommandExecutionItem; - const success = cmdItem.exit_code === 0; - notifications.push({ - sessionId, - update: { - sessionUpdate: "tool_call_update", - toolCallId: cmdItem.id, - status: success ? "completed" : "failed", - _meta: { - codex: { - toolName: "Bash", - exitCode: cmdItem.exit_code, - output: cmdItem.aggregated_output, - }, - }, - }, - }); - break; - } - - case "file_change": { - const fileItem = item as FileChangeItem; - const success = fileItem.status === "completed"; - notifications.push({ - sessionId, - update: { - sessionUpdate: "tool_call_update", - toolCallId: fileItem.id, - status: success ? "completed" : "failed", - _meta: { - codex: { - toolName: "FileChange", - changes: fileItem.changes, - }, - }, - }, - }); - break; - } - - case "mcp_tool_call": { - const mcpItem = item as McpToolCallItem; - const success = mcpItem.status === "completed"; - notifications.push({ - sessionId, - update: { - sessionUpdate: "tool_call_update", - toolCallId: mcpItem.id, - status: success ? "completed" : "failed", - _meta: { - codex: { - toolName: "McpToolCall", - server: mcpItem.server, - tool: mcpItem.tool, - result: mcpItem.result, - error: mcpItem.error, - }, - }, - }, - }); - break; - } - - case "web_search": { - const searchItem = item as WebSearchItem; - notifications.push({ - sessionId, - update: { - sessionUpdate: "tool_call_update", - toolCallId: searchItem.id, - status: "completed", - _meta: { - codex: { - toolName: "WebSearch", - query: searchItem.query, - }, - }, - }, - }); - break; - } - - case "error": { - const errorItem = item as { id: string; message?: string }; - if (errorItem.message) { - notifications.push({ - sessionId, - update: { - sessionUpdate: "agent_message_chunk", - content: { - type: "text", - text: `Error: ${errorItem.message}`, - }, - }, - }); - } - break; - } - } - - return notifications; - } - - private truncateCommand(command: string, maxLength = 50): string { - if (command.length <= maxLength) { - return command; - } - return `${command.substring(0, maxLength)}...`; - } -} diff --git a/packages/agent/src/adapters/connection.ts b/packages/agent/src/adapters/connection.ts index 863138472..a51533b2a 100644 --- a/packages/agent/src/adapters/connection.ts +++ b/packages/agent/src/adapters/connection.ts @@ -1,8 +1,7 @@ /** - * Shared ACP connection factory with framework routing. + * Shared ACP connection factory. * - * Creates ACP connections that can use different agent frameworks - * (Claude Code, OpenAI Codex) based on the configured framework. + * Creates ACP connections for the Claude Code agent. */ import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk"; @@ -11,9 +10,8 @@ import { Logger } from "@/utils/logger.js"; import { createTappedWritableStream } from "@/utils/tapped-stream.js"; import { ClaudeAcpAgent } from "./claude/claude.js"; import { createBidirectionalStreams, type StreamPair } from "./claude/utils.js"; -import { CodexAcpAgent } from "./codex/codex.js"; -export type AgentFramework = "claude" | "codex"; +export type AgentFramework = "claude"; export type AcpConnectionConfig = { framework?: AgentFramework; @@ -81,16 +79,10 @@ export function createAcpConnection( const agentStream = ndJsonStream(agentWritable, streams.agent.readable); - // Create the appropriate agent based on framework selection + // Create the Claude agent const agentConnection = new AgentSideConnection((client) => { - switch (framework) { - case "codex": - logger.info("Creating Codex agent"); - return new CodexAcpAgent(client, sessionStore); - default: - logger.info("Creating Claude agent"); - return new ClaudeAcpAgent(client, sessionStore); - } + logger.info("Creating Claude agent"); + return new ClaudeAcpAgent(client, sessionStore); }, agentStream); return { diff --git a/packages/agent/src/types.ts b/packages/agent/src/types.ts index d7799420b..0998475b5 100644 --- a/packages/agent/src/types.ts +++ b/packages/agent/src/types.ts @@ -141,7 +141,7 @@ export interface TaskExecutionOptions { // See: https://docs.claude.com/en/api/agent-sdk/permissions canUseTool?: CanUseTool; skipGitBranch?: boolean; // Skip creating a task-specific git branch - framework?: "claude" | "codex"; // Agent framework to use (defaults to "claude") + framework?: "claude"; // Agent framework to use (defaults to "claude") task?: Task; // Pre-fetched task to avoid redundant API call isReconnect?: boolean; // Session recreation - skip RUN_STARTED notification } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6358623db..7b77b3668 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -412,9 +412,6 @@ importers: '@modelcontextprotocol/sdk': specifier: ^1.23.0 version: 1.23.0(zod@3.25.76) - '@openai/codex-sdk': - specifier: 0.60.1 - version: 0.60.1 diff: specifier: ^8.0.2 version: 8.0.2 @@ -2014,10 +2011,6 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} - '@openai/codex-sdk@0.60.1': - resolution: {integrity: sha512-w7FhUXfqpzw9igTZFfKS7cUNW1FK+tT426ZkClG2X8vufW0jyGqfgPd6Uq8+gJgSTLxayF9I802FDW2KjYcfYQ==} - engines: {node: '>=18'} - '@opentelemetry/api@1.9.0': resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} @@ -9599,8 +9592,6 @@ snapshots: '@open-draft/until@2.1.0': optional: true - '@openai/codex-sdk@0.60.1': {} - '@opentelemetry/api@1.9.0': {} '@oxc-resolver/binding-android-arm-eabi@11.13.2': From 518493c277a98e7b9435f5c389691890e3adc5cb Mon Sep 17 00:00:00 2001 From: Peter Kirkham Date: Mon, 12 Jan 2026 17:53:32 -0800 Subject: [PATCH 2/2] fix: broken build --- apps/array/vite.main.config.mts | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/apps/array/vite.main.config.mts b/apps/array/vite.main.config.mts index 102994f88..d14ddf029 100644 --- a/apps/array/vite.main.config.mts +++ b/apps/array/vite.main.config.mts @@ -49,28 +49,6 @@ function fixFilenameCircularRef(): Plugin { }; } -/** - * Copy agent templates to the build directory - */ -function copyAgentTemplates(): Plugin { - return { - name: "copy-agent-templates", - writeBundle() { - const templateSrc = join( - __dirname, - "node_modules/@posthog/agent/dist/templates/plan-template.md", - ); - const templateDest = join( - __dirname, - ".vite/build/templates/plan-template.md", - ); - - mkdirSync(join(__dirname, ".vite/build/templates"), { recursive: true }); - copyFileSync(templateSrc, templateDest); - }, - }; -} - /** * Copy Claude executable to the build directory */ @@ -163,7 +141,6 @@ export default defineConfig(({ mode }) => { tsconfigPaths(), autoServicesPlugin(join(__dirname, "src/main/services")), fixFilenameCircularRef(), - copyAgentTemplates(), copyClaudeExecutable(), ], define: {