Skip to content

Commit c76e768

Browse files
authored
Migrate to ACP SDK 0.28 API (#213)
1 parent f7d1125 commit c76e768

11 files changed

Lines changed: 135 additions & 53 deletions

package-lock.json

Lines changed: 19 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,11 @@
6161
"vitest": "^4.0.10"
6262
},
6363
"dependencies": {
64-
"@agentclientprotocol/sdk": "^0.25.1",
64+
"@agentclientprotocol/sdk": "^0.28.1",
6565
"@openai/codex": "^0.141.0",
6666
"diff": "^8.0.3",
6767
"open": "^11.0.0",
68-
"vscode-jsonrpc": "^8.2.1"
68+
"vscode-jsonrpc": "^8.2.1",
69+
"zod": "^4.0.0"
6970
}
7071
}

src/ACPSessionConnection.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
import * as acp from "@agentclientprotocol/sdk";
22
import type {SessionNotification} from "@agentclientprotocol/sdk";
33

4+
export type AcpClientConnection = Pick<acp.AgentContext, "notify" | "request">;
5+
46
export class ACPSessionConnection {
5-
private readonly connection: acp.AgentSideConnection;
7+
private readonly connection: AcpClientConnection;
68
readonly sessionId: string;
79

8-
constructor(connection: acp.AgentSideConnection, sessionId: string) {
10+
constructor(connection: AcpClientConnection, sessionId: string) {
911
this.connection = connection;
1012
this.sessionId = sessionId;
1113
}
1214

1315
async update(update: UpdateSessionEvent) {
14-
await this.connection.sessionUpdate({
16+
await this.connection.notify(acp.methods.client.session.update, {
1517
sessionId: this.sessionId,
1618
update: update
1719
});
1820
}
1921
}
2022

21-
export type UpdateSessionEvent = SessionNotification["update"];
23+
export type UpdateSessionEvent = SessionNotification["update"];

src/AcpExtensions.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type {
2-
ClientSideConnection,
2+
ClientContext,
33
LoadSessionResponse,
44
NewSessionResponse,
55
ResumeSessionResponse,
@@ -61,8 +61,8 @@ export type LegacySetSessionModelExtRequest = {
6161
}
6262

6363
export async function legacySetSessionModel(
64-
connection: Pick<ClientSideConnection, "extMethod">,
64+
connection: Pick<ClientContext, "request">,
6565
params: LegacySetSessionModelRequest,
6666
): Promise<LegacySetSessionModelResponse> {
67-
return await connection.extMethod(LEGACY_SET_SESSION_MODEL_METHOD, params) as LegacySetSessionModelResponse;
67+
return await connection.request<LegacySetSessionModelResponse, LegacySetSessionModelRequest>(LEGACY_SET_SESSION_MODEL_METHOD, params);
6868
}

src/CodexAcpServer.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {CodexElicitationHandler} from "./CodexElicitationHandler";
66
import {type CodexAuthRequest, getCodexAuthMethods} from "./CodexAuthMethod";
77
import {CodexAcpClient, type SessionMetadata, type SessionMetadataWithThread} from "./CodexAcpClient";
88
import type {McpStartupResult} from "./CodexAppServerClient";
9-
import {ACPSessionConnection, type UpdateSessionEvent} from "./ACPSessionConnection";
9+
import {ACPSessionConnection, type AcpClientConnection, type UpdateSessionEvent} from "./ACPSessionConnection";
1010
import type {InputModality, ReasoningEffort} from "./app-server";
1111
import type {
1212
Account,
@@ -102,15 +102,15 @@ interface ActivePrompt {
102102
complete: () => void;
103103
}
104104

105-
export class CodexAcpServer implements acp.Agent {
105+
export class CodexAcpServer {
106106
private static readonly MODEL_NAME_TOKEN_OVERRIDES: Record<string, string> = {
107107
gpt: "GPT",
108108
mini: "Mini",
109109
codex: "Codex",
110110
};
111111

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

129129
constructor(
130-
connection: acp.AgentSideConnection,
130+
connection: AcpClientConnection,
131131
codexAcpClient: CodexAcpClient,
132132
defaultAuthRequest?: CodexAuthRequest,
133133
getExitCode?: () => number | null,
@@ -1110,7 +1110,7 @@ export class CodexAcpServer implements acp.Agent {
11101110
: mcpStartup;
11111111

11121112
for (const update of CodexEventHandler.createMcpStartupUpdates(filteredStartup)) {
1113-
await this.connection.sessionUpdate({
1113+
await this.connection.notify(acp.methods.client.session.update, {
11141114
sessionId,
11151115
update,
11161116
});
@@ -1281,7 +1281,7 @@ export class CodexAcpServer implements acp.Agent {
12811281
await this.codexAcpClient.waitForSessionNotifications(params.sessionId);
12821282
if (commandResult.turnCompleted?.turn.status === "interrupted") {
12831283
if (!this.sessionIsClosing(params.sessionId) && this.sessions.has(params.sessionId)) {
1284-
await this.connection.sessionUpdate({
1284+
await this.connection.notify(acp.methods.client.session.update, {
12851285
sessionId: params.sessionId,
12861286
update: {
12871287
sessionUpdate: "agent_message_chunk",
@@ -1382,7 +1382,7 @@ export class CodexAcpServer implements acp.Agent {
13821382
// Check if turn was interrupted (cancelled)
13831383
if (turnCompleted.turn.status === "interrupted") {
13841384
if (!this.sessionIsClosing(params.sessionId) && this.sessions.has(params.sessionId)) {
1385-
await this.connection.sessionUpdate({
1385+
await this.connection.notify(acp.methods.client.session.update, {
13861386
sessionId: params.sessionId,
13871387
update: {
13881388
sessionUpdate: "agent_message_chunk",

src/CodexApprovalHandler.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import type {
1717
import {logger} from "./Logger";
1818
import {stripShellPrefix} from "./CodexEventHandler";
1919
import {ApprovalOptionId} from "./ApprovalOptionId";
20+
import type {AcpClientConnection} from "./ACPSessionConnection";
2021

2122
type CommandDecisionOption = {
2223
option: acp.PermissionOption;
@@ -43,11 +44,11 @@ function permissionOption(
4344
}
4445

4546
export class CodexApprovalHandler implements ApprovalHandler {
46-
private readonly connection: acp.AgentSideConnection;
47+
private readonly connection: AcpClientConnection;
4748
private readonly sessionState: SessionState;
4849

4950
constructor(
50-
connection: acp.AgentSideConnection,
51+
connection: AcpClientConnection,
5152
sessionState: SessionState
5253
) {
5354
this.connection = connection;
@@ -60,7 +61,7 @@ export class CodexApprovalHandler implements ApprovalHandler {
6061
try {
6162
const sessionId = this.sessionState.sessionId;
6263
const acpRequest = this.buildCommandPermissionRequest(sessionId, params);
63-
const response = await this.connection.requestPermission(acpRequest);
64+
const response = await this.connection.request(acp.methods.client.session.requestPermission, acpRequest);
6465
return this.convertCommandResponse(params, response);
6566
} catch (error) {
6667
logger.error("Error requesting command execution permission", error);
@@ -74,7 +75,7 @@ export class CodexApprovalHandler implements ApprovalHandler {
7475
try {
7576
const sessionId = this.sessionState.sessionId;
7677
const acpRequest = this.buildFileChangePermissionRequest(sessionId, params);
77-
const response = await this.connection.requestPermission(acpRequest);
78+
const response = await this.connection.request(acp.methods.client.session.requestPermission, acpRequest);
7879
return this.convertFileChangeResponse(params, response);
7980
} catch (error) {
8081
logger.error("Error requesting file change permission", error);
@@ -88,7 +89,7 @@ export class CodexApprovalHandler implements ApprovalHandler {
8889
try {
8990
const sessionId = this.sessionState.sessionId;
9091
const acpRequest = this.buildPermissionsRequest(sessionId, params);
91-
const response = await this.connection.requestPermission(acpRequest);
92+
const response = await this.connection.request(acp.methods.client.session.requestPermission, acpRequest);
9293
return this.convertPermissionsResponse(params, response);
9394
} catch (error) {
9495
logger.error("Error requesting permissions", error);

src/CodexCommands.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type * as acp from "@agentclientprotocol/sdk";
2-
import type {AgentSideConnection, AvailableCommand} from "@agentclientprotocol/sdk";
3-
import {ACPSessionConnection} from "./ACPSessionConnection";
2+
import type {AvailableCommand} from "@agentclientprotocol/sdk";
3+
import {ACPSessionConnection, type AcpClientConnection} from "./ACPSessionConnection";
44
import type {CodexAcpClient} from "./CodexAcpClient";
55
import type {RateLimitSnapshot, ReviewTarget, SkillsListEntry, TurnCompletedNotification} from "./app-server/v2";
66
import type {SessionState} from "./CodexAcpServer";
@@ -18,12 +18,12 @@ export type CommandHandleResult =
1818
| { handled: true, turnCompleted?: TurnCompletedNotification };
1919

2020
export class CodexCommands {
21-
private readonly connection: AgentSideConnection;
21+
private readonly connection: AcpClientConnection;
2222
private readonly codexAcpClient: CodexAcpClient;
2323
private readonly runWithProcessCheck: <T>(operation: () => Promise<T>) => Promise<T>;
2424

2525
constructor(
26-
connection: AgentSideConnection,
26+
connection: AcpClientConnection,
2727
codexAcpClient: CodexAcpClient,
2828
runWithProcessCheck: <T>(operation: () => Promise<T>) => Promise<T>
2929
) {

src/CodexElicitationHandler.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
} from "./app-server/v2";
1111
import { logger } from "./Logger";
1212
import { McpApprovalOptionId } from "./McpApprovalOptionId";
13+
import type {AcpClientConnection} from "./ACPSessionConnection";
1314

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

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

88-
constructor(connection: acp.AgentSideConnection, sessionState: SessionState) {
89+
constructor(connection: AcpClientConnection, sessionState: SessionState) {
8990
this.connection = connection;
9091
this.sessionState = sessionState;
9192
}
@@ -111,11 +112,11 @@ export class CodexElicitationHandler implements ElicitationHandler {
111112
): Promise<McpServerElicitationRequestResponse> {
112113
try {
113114
const { request, correlatedCallId } = this.buildPermissionRequest(params);
114-
const response = await this.connection.requestPermission(request);
115+
const response = await this.connection.request(acp.methods.client.session.requestPermission, request);
115116
if (correlatedCallId !== undefined && response.outcome.outcome !== "cancelled") {
116117
const optionId = response.outcome.optionId;
117118
if (optionId !== McpApprovalOptionId.Decline) {
118-
await this.connection.sessionUpdate({
119+
await this.connection.notify(acp.methods.client.session.update, {
119120
sessionId: this.sessionState.sessionId,
120121
update: { sessionUpdate: "tool_call_update", toolCallId: correlatedCallId, status: "in_progress" },
121122
});

src/CodexEventHandler.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type {
66
import type {SessionState} from "./CodexAcpServer";
77
import * as acp from "@agentclientprotocol/sdk";
88
import {type PlanEntry, RequestError} from "@agentclientprotocol/sdk";
9-
import {ACPSessionConnection, type UpdateSessionEvent} from "./ACPSessionConnection";
9+
import {ACPSessionConnection, type AcpClientConnection, type UpdateSessionEvent} from "./ACPSessionConnection";
1010
import type {
1111
AccountRateLimitsUpdatedNotification,
1212
AgentMessageDeltaNotification,
@@ -59,7 +59,7 @@ export { stripShellPrefix };
5959

6060
export class CodexEventHandler {
6161

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

73-
constructor(connection: acp.AgentSideConnection, sessionState: SessionState) {
73+
constructor(connection: AcpClientConnection, sessionState: SessionState) {
7474
this.connection = connection;
7575
this.sessionState = sessionState;
7676
}

0 commit comments

Comments
 (0)