Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,11 @@
"vitest": "^4.0.10"
},
"dependencies": {
"@agentclientprotocol/sdk": "^0.25.1",
"@agentclientprotocol/sdk": "^0.28.1",
"@openai/codex": "^0.141.0",
"diff": "^8.0.3",
"open": "^11.0.0",
"vscode-jsonrpc": "^8.2.1"
"vscode-jsonrpc": "^8.2.1",
"zod": "^4.0.0"
}
}
10 changes: 6 additions & 4 deletions src/ACPSessionConnection.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import * as acp from "@agentclientprotocol/sdk";
import type {SessionNotification} from "@agentclientprotocol/sdk";

export type AcpClientConnection = Pick<acp.AgentContext, "notify" | "request">;

export class ACPSessionConnection {
private readonly connection: acp.AgentSideConnection;
private readonly connection: AcpClientConnection;
readonly sessionId: string;

constructor(connection: acp.AgentSideConnection, sessionId: string) {
constructor(connection: AcpClientConnection, sessionId: string) {
this.connection = connection;
this.sessionId = sessionId;
}

async update(update: UpdateSessionEvent) {
await this.connection.sessionUpdate({
await this.connection.notify(acp.methods.client.session.update, {
sessionId: this.sessionId,
update: update
});
}
}

export type UpdateSessionEvent = SessionNotification["update"];
export type UpdateSessionEvent = SessionNotification["update"];
6 changes: 3 additions & 3 deletions src/AcpExtensions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {
ClientSideConnection,
ClientContext,
LoadSessionResponse,
NewSessionResponse,
ResumeSessionResponse,
Expand Down Expand Up @@ -61,8 +61,8 @@ export type LegacySetSessionModelExtRequest = {
}

export async function legacySetSessionModel(
connection: Pick<ClientSideConnection, "extMethod">,
connection: Pick<ClientContext, "request">,
params: LegacySetSessionModelRequest,
): Promise<LegacySetSessionModelResponse> {
return await connection.extMethod(LEGACY_SET_SESSION_MODEL_METHOD, params) as LegacySetSessionModelResponse;
return await connection.request<LegacySetSessionModelResponse, LegacySetSessionModelRequest>(LEGACY_SET_SESSION_MODEL_METHOD, params);
}
14 changes: 7 additions & 7 deletions src/CodexAcpServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {CodexElicitationHandler} from "./CodexElicitationHandler";
import {type CodexAuthRequest, getCodexAuthMethods} from "./CodexAuthMethod";
import {CodexAcpClient, type SessionMetadata, type SessionMetadataWithThread} from "./CodexAcpClient";
import type {McpStartupResult} from "./CodexAppServerClient";
import {ACPSessionConnection, type UpdateSessionEvent} from "./ACPSessionConnection";
import {ACPSessionConnection, type AcpClientConnection, type UpdateSessionEvent} from "./ACPSessionConnection";
import type {InputModality, ReasoningEffort} from "./app-server";
import type {
Account,
Expand Down Expand Up @@ -102,15 +102,15 @@ interface ActivePrompt {
complete: () => void;
}

export class CodexAcpServer implements acp.Agent {
export class CodexAcpServer {
private static readonly MODEL_NAME_TOKEN_OVERRIDES: Record<string, string> = {
gpt: "GPT",
mini: "Mini",
codex: "Codex",
};

private readonly codexAcpClient: CodexAcpClient;
private readonly connection: acp.AgentSideConnection;
private readonly connection: AcpClientConnection;
private readonly defaultAuthRequest: CodexAuthRequest | null;
private readonly getExitCode: () => number | null;
private readonly getRecentStderr: () => string;
Expand All @@ -127,7 +127,7 @@ export class CodexAcpServer implements acp.Agent {
private readonly sessionOpenGenerations: Map<string, number>;

constructor(
connection: acp.AgentSideConnection,
connection: AcpClientConnection,
codexAcpClient: CodexAcpClient,
defaultAuthRequest?: CodexAuthRequest,
getExitCode?: () => number | null,
Expand Down Expand Up @@ -1110,7 +1110,7 @@ export class CodexAcpServer implements acp.Agent {
: mcpStartup;

for (const update of CodexEventHandler.createMcpStartupUpdates(filteredStartup)) {
await this.connection.sessionUpdate({
await this.connection.notify(acp.methods.client.session.update, {
sessionId,
update,
});
Expand Down Expand Up @@ -1281,7 +1281,7 @@ export class CodexAcpServer implements acp.Agent {
await this.codexAcpClient.waitForSessionNotifications(params.sessionId);
if (commandResult.turnCompleted?.turn.status === "interrupted") {
if (!this.sessionIsClosing(params.sessionId) && this.sessions.has(params.sessionId)) {
await this.connection.sessionUpdate({
await this.connection.notify(acp.methods.client.session.update, {
sessionId: params.sessionId,
update: {
sessionUpdate: "agent_message_chunk",
Expand Down Expand Up @@ -1382,7 +1382,7 @@ export class CodexAcpServer implements acp.Agent {
// Check if turn was interrupted (cancelled)
if (turnCompleted.turn.status === "interrupted") {
if (!this.sessionIsClosing(params.sessionId) && this.sessions.has(params.sessionId)) {
await this.connection.sessionUpdate({
await this.connection.notify(acp.methods.client.session.update, {
sessionId: params.sessionId,
update: {
sessionUpdate: "agent_message_chunk",
Expand Down
11 changes: 6 additions & 5 deletions src/CodexApprovalHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {
import {logger} from "./Logger";
import {stripShellPrefix} from "./CodexEventHandler";
import {ApprovalOptionId} from "./ApprovalOptionId";
import type {AcpClientConnection} from "./ACPSessionConnection";

type CommandDecisionOption = {
option: acp.PermissionOption;
Expand All @@ -43,11 +44,11 @@ function permissionOption(
}

export class CodexApprovalHandler implements ApprovalHandler {
private readonly connection: acp.AgentSideConnection;
private readonly connection: AcpClientConnection;
private readonly sessionState: SessionState;

constructor(
connection: acp.AgentSideConnection,
connection: AcpClientConnection,
sessionState: SessionState
) {
this.connection = connection;
Expand All @@ -60,7 +61,7 @@ export class CodexApprovalHandler implements ApprovalHandler {
try {
const sessionId = this.sessionState.sessionId;
const acpRequest = this.buildCommandPermissionRequest(sessionId, params);
const response = await this.connection.requestPermission(acpRequest);
const response = await this.connection.request(acp.methods.client.session.requestPermission, acpRequest);
return this.convertCommandResponse(params, response);
} catch (error) {
logger.error("Error requesting command execution permission", error);
Expand All @@ -74,7 +75,7 @@ export class CodexApprovalHandler implements ApprovalHandler {
try {
const sessionId = this.sessionState.sessionId;
const acpRequest = this.buildFileChangePermissionRequest(sessionId, params);
const response = await this.connection.requestPermission(acpRequest);
const response = await this.connection.request(acp.methods.client.session.requestPermission, acpRequest);
return this.convertFileChangeResponse(params, response);
} catch (error) {
logger.error("Error requesting file change permission", error);
Expand All @@ -88,7 +89,7 @@ export class CodexApprovalHandler implements ApprovalHandler {
try {
const sessionId = this.sessionState.sessionId;
const acpRequest = this.buildPermissionsRequest(sessionId, params);
const response = await this.connection.requestPermission(acpRequest);
const response = await this.connection.request(acp.methods.client.session.requestPermission, acpRequest);
return this.convertPermissionsResponse(params, response);
} catch (error) {
logger.error("Error requesting permissions", error);
Expand Down
8 changes: 4 additions & 4 deletions src/CodexCommands.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type * as acp from "@agentclientprotocol/sdk";
import type {AgentSideConnection, AvailableCommand} from "@agentclientprotocol/sdk";
import {ACPSessionConnection} from "./ACPSessionConnection";
import type {AvailableCommand} from "@agentclientprotocol/sdk";
import {ACPSessionConnection, type AcpClientConnection} from "./ACPSessionConnection";
import type {CodexAcpClient} from "./CodexAcpClient";
import type {RateLimitSnapshot, ReviewTarget, SkillsListEntry, TurnCompletedNotification} from "./app-server/v2";
import type {SessionState} from "./CodexAcpServer";
Expand All @@ -18,12 +18,12 @@ export type CommandHandleResult =
| { handled: true, turnCompleted?: TurnCompletedNotification };

export class CodexCommands {
private readonly connection: AgentSideConnection;
private readonly connection: AcpClientConnection;
private readonly codexAcpClient: CodexAcpClient;
private readonly runWithProcessCheck: <T>(operation: () => Promise<T>) => Promise<T>;

constructor(
connection: AgentSideConnection,
connection: AcpClientConnection,
codexAcpClient: CodexAcpClient,
runWithProcessCheck: <T>(operation: () => Promise<T>) => Promise<T>
) {
Expand Down
9 changes: 5 additions & 4 deletions src/CodexElicitationHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
} from "./app-server/v2";
import { logger } from "./Logger";
import { McpApprovalOptionId } from "./McpApprovalOptionId";
import type {AcpClientConnection} from "./ACPSessionConnection";

// Standard elicitation options (non-tool-call approval).
const ELICITATION_OPTIONS: acp.PermissionOption[] = [
Expand Down Expand Up @@ -66,7 +67,7 @@ function buildToolApprovalOptions(persistOptions: Set<PersistValue>): acp.Permis
}

export class CodexElicitationHandler implements ElicitationHandler {
private readonly connection: acp.AgentSideConnection;
private readonly connection: AcpClientConnection;
private readonly sessionState: SessionState;
// In Rust, the MCP elicitation handler receives ElicitationRequestEvent directly from the MCP
// protocol layer, where id is set to "mcp_tool_call_approval_<call_id>" — the call ID is extracted
Expand All @@ -85,7 +86,7 @@ export class CodexElicitationHandler implements ElicitationHandler {
// (threadId, serverName).
private readonly pendingMcpApprovals = new Map<string, string>();

constructor(connection: acp.AgentSideConnection, sessionState: SessionState) {
constructor(connection: AcpClientConnection, sessionState: SessionState) {
this.connection = connection;
this.sessionState = sessionState;
}
Expand All @@ -111,11 +112,11 @@ export class CodexElicitationHandler implements ElicitationHandler {
): Promise<McpServerElicitationRequestResponse> {
try {
const { request, correlatedCallId } = this.buildPermissionRequest(params);
const response = await this.connection.requestPermission(request);
const response = await this.connection.request(acp.methods.client.session.requestPermission, request);
if (correlatedCallId !== undefined && response.outcome.outcome !== "cancelled") {
const optionId = response.outcome.optionId;
if (optionId !== McpApprovalOptionId.Decline) {
await this.connection.sessionUpdate({
await this.connection.notify(acp.methods.client.session.update, {
sessionId: this.sessionState.sessionId,
update: { sessionUpdate: "tool_call_update", toolCallId: correlatedCallId, status: "in_progress" },
});
Expand Down
6 changes: 3 additions & 3 deletions src/CodexEventHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {
import type {SessionState} from "./CodexAcpServer";
import * as acp from "@agentclientprotocol/sdk";
import {type PlanEntry, RequestError} from "@agentclientprotocol/sdk";
import {ACPSessionConnection, type UpdateSessionEvent} from "./ACPSessionConnection";
import {ACPSessionConnection, type AcpClientConnection, type UpdateSessionEvent} from "./ACPSessionConnection";
import type {
AccountRateLimitsUpdatedNotification,
AgentMessageDeltaNotification,
Expand Down Expand Up @@ -59,7 +59,7 @@ export { stripShellPrefix };

export class CodexEventHandler {

private readonly connection: acp.AgentSideConnection;
private readonly connection: AcpClientConnection;
private readonly sessionState: SessionState;
private failure: RequestError | null = null;
private readonly activeFuzzyFileSearchSessions = new Set<string>();
Expand All @@ -70,7 +70,7 @@ export class CodexEventHandler {
private readonly terminalCommandIds = new Set<string>();
private readonly terminalCommandOutputIds = new Set<string>();

constructor(connection: acp.AgentSideConnection, sessionState: SessionState) {
constructor(connection: AcpClientConnection, sessionState: SessionState) {
this.connection = connection;
this.sessionState = sessionState;
}
Expand Down
Loading
Loading