@agentclientprotocol/codex-acp 0.0.44 → 0.0.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1129 -309
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -14441,6 +14441,7 @@ var AGENT_METHODS = {
14441
14441
  document_did_save: "document/didSave",
14442
14442
  initialize: "initialize",
14443
14443
  logout: "logout",
14444
+ mcp_message: "mcp/message",
14444
14445
  nes_accept: "nes/accept",
14445
14446
  nes_close: "nes/close",
14446
14447
  nes_reject: "nes/reject",
@@ -14451,6 +14452,7 @@ var AGENT_METHODS = {
14451
14452
  providers_set: "providers/set",
14452
14453
  session_cancel: "session/cancel",
14453
14454
  session_close: "session/close",
14455
+ session_delete: "session/delete",
14454
14456
  session_fork: "session/fork",
14455
14457
  session_list: "session/list",
14456
14458
  session_load: "session/load",
@@ -14466,6 +14468,9 @@ var CLIENT_METHODS = {
14466
14468
  elicitation_create: "elicitation/create",
14467
14469
  fs_read_text_file: "fs/read_text_file",
14468
14470
  fs_write_text_file: "fs/write_text_file",
14471
+ mcp_connect: "mcp/connect",
14472
+ mcp_disconnect: "mcp/disconnect",
14473
+ mcp_message: "mcp/message",
14469
14474
  session_request_permission: "session/request_permission",
14470
14475
  session_update: "session/update",
14471
14476
  terminal_create: "terminal/create",
@@ -14551,6 +14556,9 @@ var zCreateTerminalResponse = external_exports.object({
14551
14556
  _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
14552
14557
  terminalId: external_exports.string()
14553
14558
  });
14559
+ var zDeleteSessionResponse = external_exports.object({
14560
+ _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish()
14561
+ });
14554
14562
  var zDiff = external_exports.object({
14555
14563
  _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
14556
14564
  newText: external_exports.string(),
@@ -14564,6 +14572,9 @@ var zDisableProvidersRequest = external_exports.object({
14564
14572
  var zDisableProvidersResponse = external_exports.object({
14565
14573
  _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish()
14566
14574
  });
14575
+ var zDisconnectMcpResponse = external_exports.object({
14576
+ _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish()
14577
+ });
14567
14578
  var zElicitationContentValue = external_exports.union([
14568
14579
  external_exports.string(),
14569
14580
  external_exports.number(),
@@ -14669,7 +14680,6 @@ var zListProvidersRequest = external_exports.object({
14669
14680
  });
14670
14681
  var zListSessionsRequest = external_exports.object({
14671
14682
  _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
14672
- additionalDirectories: external_exports.array(external_exports.string()).optional(),
14673
14683
  cursor: external_exports.string().nullish(),
14674
14684
  cwd: external_exports.string().nullish()
14675
14685
  });
@@ -14696,9 +14706,29 @@ var zLogoutResponse = external_exports.object({
14696
14706
  });
14697
14707
  var zMcpCapabilities = external_exports.object({
14698
14708
  _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
14709
+ acp: external_exports.boolean().optional().default(false),
14699
14710
  http: external_exports.boolean().optional().default(false),
14700
14711
  sse: external_exports.boolean().optional().default(false)
14701
14712
  });
14713
+ var zMcpConnectionId = external_exports.string();
14714
+ var zConnectMcpResponse = external_exports.object({
14715
+ _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
14716
+ connectionId: zMcpConnectionId
14717
+ });
14718
+ var zDisconnectMcpRequest = external_exports.object({
14719
+ _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
14720
+ connectionId: zMcpConnectionId
14721
+ });
14722
+ var zMcpServerAcpId = external_exports.string();
14723
+ var zConnectMcpRequest = external_exports.object({
14724
+ _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
14725
+ acpId: zMcpServerAcpId
14726
+ });
14727
+ var zMcpServerAcp = external_exports.object({
14728
+ _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
14729
+ id: zMcpServerAcpId,
14730
+ name: external_exports.string()
14731
+ });
14702
14732
  var zMcpServerHttp = external_exports.object({
14703
14733
  _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
14704
14734
  headers: external_exports.array(zHttpHeader),
@@ -14725,8 +14755,24 @@ var zMcpServer = external_exports.union([
14725
14755
  zMcpServerSse.and(external_exports.object({
14726
14756
  type: external_exports.literal("sse")
14727
14757
  })),
14758
+ zMcpServerAcp.and(external_exports.object({
14759
+ type: external_exports.literal("acp")
14760
+ })),
14728
14761
  zMcpServerStdio
14729
14762
  ]);
14763
+ var zMessageMcpNotification = external_exports.object({
14764
+ _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
14765
+ connectionId: zMcpConnectionId,
14766
+ method: external_exports.string(),
14767
+ params: external_exports.record(external_exports.string(), external_exports.unknown()).nullish()
14768
+ });
14769
+ var zMessageMcpRequest = external_exports.object({
14770
+ _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
14771
+ connectionId: zMcpConnectionId,
14772
+ method: external_exports.string(),
14773
+ params: external_exports.record(external_exports.string(), external_exports.unknown()).nullish()
14774
+ });
14775
+ var zMessageMcpResponse = external_exports.unknown();
14730
14776
  var zModelId = external_exports.string();
14731
14777
  var zModelInfo = external_exports.object({
14732
14778
  _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
@@ -15134,6 +15180,9 @@ var zConfigOptionUpdate = external_exports.object({
15134
15180
  _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
15135
15181
  configOptions: external_exports.array(zSessionConfigOption)
15136
15182
  });
15183
+ var zSessionDeleteCapabilities = external_exports.object({
15184
+ _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish()
15185
+ });
15137
15186
  var zSessionForkCapabilities = external_exports.object({
15138
15187
  _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish()
15139
15188
  });
@@ -15164,6 +15213,10 @@ var zCreateTerminalRequest = external_exports.object({
15164
15213
  outputByteLimit: external_exports.number().nullish(),
15165
15214
  sessionId: zSessionId
15166
15215
  });
15216
+ var zDeleteSessionRequest = external_exports.object({
15217
+ _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
15218
+ sessionId: zSessionId
15219
+ });
15167
15220
  var zDidCloseDocumentNotification = external_exports.object({
15168
15221
  _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
15169
15222
  sessionId: zSessionId,
@@ -15313,6 +15366,7 @@ var zSessionCapabilities = external_exports.object({
15313
15366
  _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
15314
15367
  additionalDirectories: zSessionAdditionalDirectoriesCapabilities.nullish(),
15315
15368
  close: zSessionCloseCapabilities.nullish(),
15369
+ delete: zSessionDeleteCapabilities.nullish(),
15316
15370
  fork: zSessionForkCapabilities.nullish(),
15317
15371
  list: zSessionListCapabilities.nullish(),
15318
15372
  resume: zSessionResumeCapabilities.nullish()
@@ -15455,6 +15509,7 @@ var zClientNotification = external_exports.object({
15455
15509
  zDidFocusDocumentNotification,
15456
15510
  zAcceptNesNotification,
15457
15511
  zRejectNesNotification,
15512
+ zMessageMcpNotification,
15458
15513
  zExtNotification
15459
15514
  ]).nullish()
15460
15515
  });
@@ -15487,7 +15542,11 @@ var zAgentCapabilities = external_exports.object({
15487
15542
  _meta: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
15488
15543
  auth: zAgentAuthCapabilities.optional().default({}),
15489
15544
  loadSession: external_exports.boolean().optional().default(false),
15490
- mcpCapabilities: zMcpCapabilities.optional().default({ http: false, sse: false }),
15545
+ mcpCapabilities: zMcpCapabilities.optional().default({
15546
+ acp: false,
15547
+ http: false,
15548
+ sse: false
15549
+ }),
15491
15550
  nes: zNesCapabilities.nullish(),
15492
15551
  positionEncoding: zPositionEncodingKind.nullish(),
15493
15552
  promptCapabilities: zPromptCapabilities.optional().default({
@@ -15503,7 +15562,11 @@ var zInitializeResponse = external_exports.object({
15503
15562
  agentCapabilities: zAgentCapabilities.optional().default({
15504
15563
  auth: {},
15505
15564
  loadSession: false,
15506
- mcpCapabilities: { http: false, sse: false },
15565
+ mcpCapabilities: {
15566
+ acp: false,
15567
+ http: false,
15568
+ sse: false
15569
+ },
15507
15570
  promptCapabilities: {
15508
15571
  audio: false,
15509
15572
  embeddedContext: false,
@@ -15734,6 +15797,7 @@ var zAgentResponse = external_exports.union([
15734
15797
  zNewSessionResponse,
15735
15798
  zLoadSessionResponse,
15736
15799
  zListSessionsResponse,
15800
+ zDeleteSessionResponse,
15737
15801
  zForkSessionResponse,
15738
15802
  zResumeSessionResponse,
15739
15803
  zCloseSessionResponse,
@@ -15744,7 +15808,8 @@ var zAgentResponse = external_exports.union([
15744
15808
  zStartNesResponse,
15745
15809
  zSuggestNesResponse,
15746
15810
  zCloseNesResponse,
15747
- zExtResponse
15811
+ zExtResponse,
15812
+ zMessageMcpResponse
15748
15813
  ])
15749
15814
  }),
15750
15815
  external_exports.object({
@@ -15803,6 +15868,7 @@ var zAgentNotification = external_exports.object({
15803
15868
  params: external_exports.union([
15804
15869
  zSessionNotification,
15805
15870
  zCompleteElicitationNotification,
15871
+ zMessageMcpNotification,
15806
15872
  zExtNotification
15807
15873
  ]).nullish()
15808
15874
  });
@@ -15841,6 +15907,7 @@ var zClientRequest = external_exports.object({
15841
15907
  zNewSessionRequest,
15842
15908
  zLoadSessionRequest,
15843
15909
  zListSessionsRequest,
15910
+ zDeleteSessionRequest,
15844
15911
  zForkSessionRequest,
15845
15912
  zResumeSessionRequest,
15846
15913
  zCloseSessionRequest,
@@ -15851,6 +15918,7 @@ var zClientRequest = external_exports.object({
15851
15918
  zStartNesRequest,
15852
15919
  zSuggestNesRequest,
15853
15920
  zCloseNesRequest,
15921
+ zMessageMcpRequest,
15854
15922
  zExtRequest
15855
15923
  ]).nullish()
15856
15924
  });
@@ -15873,6 +15941,9 @@ var zAgentRequest = external_exports.object({
15873
15941
  zWaitForTerminalExitRequest,
15874
15942
  zKillTerminalRequest,
15875
15943
  zCreateElicitationRequest,
15944
+ zConnectMcpRequest,
15945
+ zMessageMcpRequest,
15946
+ zDisconnectMcpRequest,
15876
15947
  zExtRequest
15877
15948
  ]).nullish()
15878
15949
  });
@@ -15892,7 +15963,10 @@ var zClientResponse = external_exports.union([
15892
15963
  zWaitForTerminalExitResponse,
15893
15964
  zKillTerminalResponse,
15894
15965
  zCreateElicitationResponse,
15895
- zExtResponse
15966
+ zConnectMcpResponse,
15967
+ zDisconnectMcpResponse,
15968
+ zExtResponse,
15969
+ zMessageMcpResponse
15896
15970
  ])
15897
15971
  }),
15898
15972
  external_exports.object({
@@ -15967,6 +16041,15 @@ function ndJsonStream(output, input) {
15967
16041
  }
15968
16042
 
15969
16043
  // node_modules/@agentclientprotocol/sdk/dist/acp.js
16044
+ function emptyObjectResponse(response) {
16045
+ return response ?? {};
16046
+ }
16047
+ function rejectedPromise(error40) {
16048
+ const promise2 = Promise.reject(error40);
16049
+ promise2.catch(() => {
16050
+ });
16051
+ return promise2;
16052
+ }
15970
16053
  var AgentSideConnection = class {
15971
16054
  connection;
15972
16055
  /**
@@ -16007,6 +16090,14 @@ var AgentSideConnection = class {
16007
16090
  const validatedParams = zListSessionsRequest.parse(params);
16008
16091
  return agent.listSessions(validatedParams);
16009
16092
  }
16093
+ case AGENT_METHODS.session_delete: {
16094
+ if (!agent.unstable_deleteSession) {
16095
+ throw RequestError.methodNotFound(method);
16096
+ }
16097
+ const validatedParams = zDeleteSessionRequest.parse(params);
16098
+ const result = await agent.unstable_deleteSession(validatedParams);
16099
+ return result ?? {};
16100
+ }
16010
16101
  case AGENT_METHODS.session_fork: {
16011
16102
  if (!agent.unstable_forkSession) {
16012
16103
  throw RequestError.methodNotFound(method);
@@ -16191,8 +16282,8 @@ var AgentSideConnection = class {
16191
16282
  *
16192
16283
  * See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
16193
16284
  */
16194
- async sessionUpdate(params) {
16195
- return await this.connection.sendNotification(CLIENT_METHODS.session_update, params);
16285
+ sessionUpdate(params) {
16286
+ return this.connection.sendNotification(CLIENT_METHODS.session_update, params);
16196
16287
  }
16197
16288
  /**
16198
16289
  * Requests permission from the user for a tool call operation.
@@ -16206,8 +16297,8 @@ var AgentSideConnection = class {
16206
16297
  *
16207
16298
  * See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
16208
16299
  */
16209
- async requestPermission(params) {
16210
- return await this.connection.sendRequest(CLIENT_METHODS.session_request_permission, params);
16300
+ requestPermission(params) {
16301
+ return this.connection.sendRequest(CLIENT_METHODS.session_request_permission, params);
16211
16302
  }
16212
16303
  /**
16213
16304
  * Reads content from a text file in the client's file system.
@@ -16217,8 +16308,8 @@ var AgentSideConnection = class {
16217
16308
  *
16218
16309
  * See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
16219
16310
  */
16220
- async readTextFile(params) {
16221
- return await this.connection.sendRequest(CLIENT_METHODS.fs_read_text_file, params);
16311
+ readTextFile(params) {
16312
+ return this.connection.sendRequest(CLIENT_METHODS.fs_read_text_file, params);
16222
16313
  }
16223
16314
  /**
16224
16315
  * Writes content to a text file in the client's file system.
@@ -16228,8 +16319,8 @@ var AgentSideConnection = class {
16228
16319
  *
16229
16320
  * See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
16230
16321
  */
16231
- async writeTextFile(params) {
16232
- return await this.connection.sendRequest(CLIENT_METHODS.fs_write_text_file, params) ?? {};
16322
+ writeTextFile(params) {
16323
+ return this.connection.sendRequest(CLIENT_METHODS.fs_write_text_file, params, emptyObjectResponse);
16233
16324
  }
16234
16325
  /**
16235
16326
  * Executes a command in a new terminal.
@@ -16243,9 +16334,8 @@ var AgentSideConnection = class {
16243
16334
  * @param params - The terminal creation parameters
16244
16335
  * @returns A handle to control and monitor the terminal
16245
16336
  */
16246
- async createTerminal(params) {
16247
- const response = await this.connection.sendRequest(CLIENT_METHODS.terminal_create, params);
16248
- return new TerminalHandle(response.terminalId, params.sessionId, this.connection);
16337
+ createTerminal(params) {
16338
+ return this.connection.sendRequest(CLIENT_METHODS.terminal_create, params, (response) => new TerminalHandle(response.terminalId, params.sessionId, this.connection));
16249
16339
  }
16250
16340
  /**
16251
16341
  * **UNSTABLE**
@@ -16256,8 +16346,8 @@ var AgentSideConnection = class {
16256
16346
  *
16257
16347
  * @experimental
16258
16348
  */
16259
- async unstable_createElicitation(params) {
16260
- return await this.connection.sendRequest(CLIENT_METHODS.elicitation_create, params);
16349
+ unstable_createElicitation(params) {
16350
+ return this.connection.sendRequest(CLIENT_METHODS.elicitation_create, params);
16261
16351
  }
16262
16352
  /**
16263
16353
  * **UNSTABLE**
@@ -16268,24 +16358,24 @@ var AgentSideConnection = class {
16268
16358
  *
16269
16359
  * @experimental
16270
16360
  */
16271
- async unstable_completeElicitation(params) {
16272
- return await this.connection.sendNotification(CLIENT_METHODS.elicitation_complete, params);
16361
+ unstable_completeElicitation(params) {
16362
+ return this.connection.sendNotification(CLIENT_METHODS.elicitation_complete, params);
16273
16363
  }
16274
16364
  /**
16275
16365
  * Extension method
16276
16366
  *
16277
16367
  * Allows the Agent to send an arbitrary request that is not part of the ACP spec.
16278
16368
  */
16279
- async extMethod(method, params) {
16280
- return await this.connection.sendRequest(method, params);
16369
+ extMethod(method, params) {
16370
+ return this.connection.sendRequest(method, params);
16281
16371
  }
16282
16372
  /**
16283
16373
  * Extension notification
16284
16374
  *
16285
16375
  * Allows the Agent to send an arbitrary notification that is not part of the ACP spec.
16286
16376
  */
16287
- async extNotification(method, params) {
16288
- return await this.connection.sendNotification(method, params);
16377
+ extNotification(method, params) {
16378
+ return this.connection.sendNotification(method, params);
16289
16379
  }
16290
16380
  /**
16291
16381
  * AbortSignal that aborts when the connection closes.
@@ -16349,8 +16439,8 @@ var TerminalHandle = class {
16349
16439
  /**
16350
16440
  * Gets the current terminal output without waiting for the command to exit.
16351
16441
  */
16352
- async currentOutput() {
16353
- return await this.connection.sendRequest(CLIENT_METHODS.terminal_output, {
16442
+ currentOutput() {
16443
+ return this.connection.sendRequest(CLIENT_METHODS.terminal_output, {
16354
16444
  sessionId: this.sessionId,
16355
16445
  terminalId: this.id
16356
16446
  });
@@ -16358,8 +16448,8 @@ var TerminalHandle = class {
16358
16448
  /**
16359
16449
  * Waits for the terminal command to complete and returns its exit status.
16360
16450
  */
16361
- async waitForExit() {
16362
- return await this.connection.sendRequest(CLIENT_METHODS.terminal_wait_for_exit, {
16451
+ waitForExit() {
16452
+ return this.connection.sendRequest(CLIENT_METHODS.terminal_wait_for_exit, {
16363
16453
  sessionId: this.sessionId,
16364
16454
  terminalId: this.id
16365
16455
  });
@@ -16374,11 +16464,11 @@ var TerminalHandle = class {
16374
16464
  *
16375
16465
  * Useful for implementing timeouts or cancellation.
16376
16466
  */
16377
- async kill() {
16378
- return await this.connection.sendRequest(CLIENT_METHODS.terminal_kill, {
16467
+ kill() {
16468
+ return this.connection.sendRequest(CLIENT_METHODS.terminal_kill, {
16379
16469
  sessionId: this.sessionId,
16380
16470
  terminalId: this.id
16381
- }) ?? {};
16471
+ }, emptyObjectResponse);
16382
16472
  }
16383
16473
  /**
16384
16474
  * Releases the terminal and frees all associated resources.
@@ -16392,11 +16482,11 @@ var TerminalHandle = class {
16392
16482
  *
16393
16483
  * **Important:** Always call this method when done with the terminal.
16394
16484
  */
16395
- async release() {
16396
- return await this.connection.sendRequest(CLIENT_METHODS.terminal_release, {
16485
+ release() {
16486
+ return this.connection.sendRequest(CLIENT_METHODS.terminal_release, {
16397
16487
  sessionId: this.sessionId,
16398
16488
  terminalId: this.id
16399
- }) ?? {};
16489
+ }, emptyObjectResponse);
16400
16490
  }
16401
16491
  async [Symbol.asyncDispose]() {
16402
16492
  await this.release();
@@ -16581,25 +16671,36 @@ var Connection = class {
16581
16671
  console.error("Got response to unknown request", response.id);
16582
16672
  }
16583
16673
  }
16584
- sendRequest(method, params) {
16585
- this.throwIfClosed();
16674
+ sendRequest(method, params, mapResponse) {
16675
+ if (this.abortController.signal.aborted) {
16676
+ return rejectedPromise(this.closedReason());
16677
+ }
16586
16678
  const id = this.nextRequestId++;
16587
16679
  const responsePromise = new Promise((resolve, reject) => {
16588
- this.pendingResponses.set(id, { resolve, reject });
16680
+ this.pendingResponses.set(id, {
16681
+ resolve: (response) => {
16682
+ try {
16683
+ resolve(mapResponse ? mapResponse(response) : response);
16684
+ } catch (error40) {
16685
+ reject(error40);
16686
+ }
16687
+ },
16688
+ reject
16689
+ });
16589
16690
  });
16590
16691
  responsePromise.catch(() => {
16591
16692
  });
16592
16693
  void this.sendMessage({ jsonrpc: "2.0", id, method, params });
16593
16694
  return responsePromise;
16594
16695
  }
16595
- async sendNotification(method, params) {
16596
- this.throwIfClosed();
16597
- await this.sendMessage({ jsonrpc: "2.0", method, params });
16598
- }
16599
- throwIfClosed() {
16696
+ sendNotification(method, params) {
16600
16697
  if (this.abortController.signal.aborted) {
16601
- throw this.abortController.signal.reason ?? new Error("ACP connection closed");
16698
+ return rejectedPromise(this.closedReason());
16602
16699
  }
16700
+ return this.sendMessage({ jsonrpc: "2.0", method, params });
16701
+ }
16702
+ closedReason() {
16703
+ return this.abortController.signal.reason ?? new Error("ACP connection closed");
16603
16704
  }
16604
16705
  async sendMessage(message) {
16605
16706
  this.writeQueue = this.writeQueue.then(async () => {
@@ -17218,6 +17319,30 @@ function applyStructuredPatch(source, patch, options = {}) {
17218
17319
  return resultLines.join("\n");
17219
17320
  }
17220
17321
 
17322
+ // node_modules/diff/libesm/patch/reverse.js
17323
+ function reversePatch(structuredPatch) {
17324
+ if (Array.isArray(structuredPatch)) {
17325
+ return structuredPatch.map((patch) => reversePatch(patch)).reverse();
17326
+ }
17327
+ return Object.assign(Object.assign({}, structuredPatch), { oldFileName: structuredPatch.newFileName, oldHeader: structuredPatch.newHeader, newFileName: structuredPatch.oldFileName, newHeader: structuredPatch.oldHeader, hunks: structuredPatch.hunks.map((hunk) => {
17328
+ return {
17329
+ oldLines: hunk.newLines,
17330
+ oldStart: hunk.newStart,
17331
+ newLines: hunk.oldLines,
17332
+ newStart: hunk.oldStart,
17333
+ lines: hunk.lines.map((l) => {
17334
+ if (l.startsWith("-")) {
17335
+ return `+${l.slice(1)}`;
17336
+ }
17337
+ if (l.startsWith("+")) {
17338
+ return `-${l.slice(1)}`;
17339
+ }
17340
+ return l;
17341
+ })
17342
+ };
17343
+ }) });
17344
+ }
17345
+
17221
17346
  // src/CodexToolCallMapper.ts
17222
17347
  import { readFile } from "node:fs/promises";
17223
17348
  import path2 from "node:path";
@@ -17369,7 +17494,7 @@ function createCommandActionEvent(id, status, cwd, commandAction) {
17369
17494
  toolCallId: id,
17370
17495
  status: acpStatus,
17371
17496
  kind: "read",
17372
- title: "Read file",
17497
+ title: `Read file '${commandAction.path}'`,
17373
17498
  locations: [{ path: commandAction.path }]
17374
17499
  };
17375
17500
  } else if (commandAction.type === "search") {
@@ -17420,80 +17545,93 @@ function createSearchTitle(query, path5) {
17420
17545
  return "Search";
17421
17546
  }
17422
17547
  async function createPatchContent(change) {
17423
- if (change.kind.type === "add" && !isUnifiedDiff(change.diff)) {
17424
- return {
17425
- type: "diff",
17426
- oldText: null,
17427
- newText: change.diff,
17428
- path: change.path,
17429
- _meta: {
17430
- kind: "add"
17431
- }
17432
- };
17433
- }
17434
- if (change.kind.type === "delete") {
17435
- const oldContent2 = await readFile(change.path, { encoding: "utf8" }).catch(
17436
- () => isUnifiedDiff(change.diff) ? patchToDeletedContent(change.diff) : change.diff
17437
- );
17438
- return {
17439
- type: "diff",
17440
- oldText: oldContent2,
17441
- newText: "",
17442
- path: change.path,
17443
- _meta: {
17444
- kind: "delete"
17445
- }
17446
- };
17447
- }
17448
- const oldContent = change.kind.type === "add" ? "" : await readFile(change.path, { encoding: "utf8" }).catch(() => null);
17449
- if (oldContent === null) {
17450
- return null;
17451
- }
17452
- const newContent = applyPatch(oldContent, change.diff);
17453
- if (newContent === false) {
17548
+ try {
17549
+ switch (change.kind.type) {
17550
+ case "add":
17551
+ return await createAddFileContent(change);
17552
+ case "delete":
17553
+ return await createDeleteFileContent(change);
17554
+ case "update":
17555
+ return await createUpdateFileContent(change);
17556
+ }
17557
+ } catch (error40) {
17558
+ logger.log(`Error processing file update change: ${error40}`);
17454
17559
  return null;
17455
17560
  }
17561
+ }
17562
+ async function createAddFileContent(change) {
17456
17563
  return {
17457
17564
  type: "diff",
17458
- oldText: change.kind.type === "add" ? null : oldContent,
17459
- newText: newContent,
17565
+ oldText: null,
17566
+ newText: change.diff,
17567
+ // app-server always returns file content instead of diff
17460
17568
  path: change.path,
17461
17569
  _meta: {
17462
- kind: change.kind.type
17570
+ kind: "add"
17463
17571
  }
17464
17572
  };
17465
17573
  }
17466
- function isUnifiedDiff(content) {
17467
- return content.startsWith("--- ") || content.includes("\n--- ");
17468
- }
17469
- function patchToDeletedContent(unifiedDiff) {
17470
- try {
17471
- const [patch] = parsePatch(unifiedDiff);
17472
- if (!patch || patch.hunks.length === 0) {
17473
- return null;
17474
- }
17475
- const oldLines = [];
17476
- let hasNoTrailingNewlineMarker = false;
17477
- for (const hunk of patch.hunks) {
17478
- for (const line of hunk.lines) {
17479
- if (line === "\") {
17480
- hasNoTrailingNewlineMarker = true;
17481
- continue;
17482
- }
17483
- if (line.startsWith("-") || line.startsWith(" ")) {
17484
- oldLines.push(line.slice(1));
17574
+ async function createUpdateFileContent(change) {
17575
+ if (change.kind.type !== "update") return null;
17576
+ const unifiedDiff = recoverCorruptedDiff(change.diff);
17577
+ const movePath = change.kind.move_path;
17578
+ const oldContent = await readFileContent(change.path);
17579
+ if (oldContent !== null) {
17580
+ const patchedContent = applyPatch(oldContent, unifiedDiff);
17581
+ if (patchedContent === false) {
17582
+ const revertedPatch2 = revertPatch(unifiedDiff);
17583
+ if (revertedPatch2) {
17584
+ const revertedContent2 = applyPatch(oldContent, revertedPatch2);
17585
+ if (revertedContent2 !== false) {
17586
+ return createUpdateDiffContent(change.path, revertedContent2, oldContent);
17485
17587
  }
17486
17588
  }
17589
+ return null;
17487
17590
  }
17488
- if (oldLines.length === 0) {
17489
- return "";
17490
- }
17491
- const oldText = oldLines.join("\n");
17492
- return hasNoTrailingNewlineMarker || !unifiedDiff.endsWith("\n") ? oldText : `${oldText}
17493
- `;
17494
- } catch {
17495
- return null;
17591
+ return createUpdateDiffContent(movePath ?? change.path, oldContent, patchedContent);
17496
17592
  }
17593
+ if (!movePath) return null;
17594
+ const newContent = await readFileContent(movePath);
17595
+ if (newContent === null) return null;
17596
+ const revertedPatch = revertPatch(unifiedDiff);
17597
+ if (!revertedPatch) return null;
17598
+ const revertedContent = applyPatch(newContent, revertedPatch);
17599
+ if (revertedContent === false) return null;
17600
+ return createUpdateDiffContent(movePath, revertedContent, newContent);
17601
+ }
17602
+ function revertPatch(unifiedDiff) {
17603
+ const [patch] = parsePatch(unifiedDiff);
17604
+ if (!patch) return null;
17605
+ return reversePatch(patch);
17606
+ }
17607
+ function createUpdateDiffContent(path5, oldText, newText) {
17608
+ return {
17609
+ type: "diff",
17610
+ oldText,
17611
+ newText,
17612
+ path: path5,
17613
+ _meta: {
17614
+ kind: "update"
17615
+ }
17616
+ };
17617
+ }
17618
+ async function createDeleteFileContent(change) {
17619
+ return {
17620
+ type: "diff",
17621
+ oldText: change.diff,
17622
+ // app-server always returns file content instead of diff
17623
+ newText: "",
17624
+ path: change.path,
17625
+ _meta: {
17626
+ kind: "delete"
17627
+ }
17628
+ };
17629
+ }
17630
+ async function readFileContent(filePath) {
17631
+ return await readFile(filePath, { encoding: "utf8" }).catch(() => null);
17632
+ }
17633
+ function recoverCorruptedDiff(diff) {
17634
+ return diff.replace(/\n\nMoved to: .*$/, "");
17497
17635
  }
17498
17636
 
17499
17637
  // src/CodexEventHandler.ts
@@ -17536,6 +17674,27 @@ var CodexEventHandler = class {
17536
17674
  return null;
17537
17675
  case "thread/tokenUsage/updated":
17538
17676
  return this.createUsageUpdate(notification.params);
17677
+ case "thread/name/updated":
17678
+ return {
17679
+ sessionUpdate: "session_info_update",
17680
+ title: notification.params.threadName ?? null
17681
+ };
17682
+ case "thread/status/changed":
17683
+ return this.createCodexSessionInfoUpdate({
17684
+ threadStatus: notification.params.status
17685
+ });
17686
+ case "thread/archived":
17687
+ return this.createCodexSessionInfoUpdate({
17688
+ archived: true
17689
+ });
17690
+ case "thread/unarchived":
17691
+ return this.createCodexSessionInfoUpdate({
17692
+ archived: false
17693
+ });
17694
+ case "thread/closed":
17695
+ return this.createCodexSessionInfoUpdate({
17696
+ closed: true
17697
+ });
17539
17698
  case "item/commandExecution/outputDelta":
17540
17699
  return this.createCommandOutputDeltaEvent(notification.params);
17541
17700
  case "item/mcpToolCall/progress":
@@ -17582,10 +17741,6 @@ var CodexEventHandler = class {
17582
17741
  case "serverRequest/resolved":
17583
17742
  case "model/verification":
17584
17743
  case "windows/worldWritableWarning":
17585
- case "thread/status/changed":
17586
- case "thread/archived":
17587
- case "thread/unarchived":
17588
- case "thread/closed":
17589
17744
  case "thread/realtime/started":
17590
17745
  case "thread/realtime/itemAdded":
17591
17746
  case "thread/realtime/transcript/delta":
@@ -17602,15 +17757,25 @@ var CodexEventHandler = class {
17602
17757
  case "externalAgentConfig/import/completed":
17603
17758
  case "rawResponseItem/completed":
17604
17759
  case "thread/started":
17605
- case "thread/name/updated":
17606
17760
  case "item/plan/delta":
17607
17761
  case "thread/goal/updated":
17608
17762
  case "thread/goal/cleared":
17609
17763
  case "remoteControl/status/changed":
17610
17764
  case "app/list/updated":
17765
+ case "thread/settings/updated":
17766
+ case "process/outputDelta":
17767
+ case "process/exited":
17611
17768
  return null;
17612
17769
  }
17613
17770
  }
17771
+ createCodexSessionInfoUpdate(codexMetadata) {
17772
+ return {
17773
+ sessionUpdate: "session_info_update",
17774
+ _meta: {
17775
+ codex: codexMetadata
17776
+ }
17777
+ };
17778
+ }
17614
17779
  async createTextEvent(event) {
17615
17780
  return {
17616
17781
  sessionUpdate: "agent_message_chunk",
@@ -17934,42 +18099,28 @@ var CodexApprovalHandler = class {
17934
18099
  }
17935
18100
  }
17936
18101
  buildCommandPermissionRequest(sessionId, params) {
17937
- const reasonContent = this.createContentFromReason(params.reason ?? null);
17938
18102
  return {
17939
18103
  sessionId,
17940
18104
  toolCall: {
17941
18105
  toolCallId: params.itemId,
17942
18106
  kind: "execute",
17943
18107
  status: "pending",
17944
- content: reasonContent ? [reasonContent] : null,
17945
18108
  rawInput: params.command ? { command: stripShellPrefix(params.command), cwd: params.cwd } : null
17946
18109
  },
17947
- options: APPROVAL_OPTIONS
17948
- };
17949
- }
17950
- createContentFromReason(reason) {
17951
- if (reason === null || reason === "") {
17952
- return null;
17953
- }
17954
- return {
17955
- type: "content",
17956
- content: {
17957
- type: "text",
17958
- text: reason
17959
- }
18110
+ options: APPROVAL_OPTIONS,
18111
+ _meta: { codex: { params } }
17960
18112
  };
17961
18113
  }
17962
18114
  buildFileChangePermissionRequest(sessionId, params) {
17963
- const reasonContent = this.createContentFromReason(params.reason ?? null);
17964
18115
  return {
17965
18116
  sessionId,
17966
18117
  toolCall: {
17967
18118
  toolCallId: params.itemId,
17968
18119
  kind: "edit",
17969
- status: "pending",
17970
- content: reasonContent ? [reasonContent] : null
18120
+ status: "pending"
17971
18121
  },
17972
- options: APPROVAL_OPTIONS
18122
+ options: APPROVAL_OPTIONS,
18123
+ _meta: { codex: { params } }
17973
18124
  };
17974
18125
  }
17975
18126
  convertCommandResponse(response) {
@@ -18000,12 +18151,19 @@ var CodexApprovalHandler = class {
18000
18151
  }
18001
18152
  };
18002
18153
 
18154
+ // src/McpApprovalOptionId.ts
18155
+ var McpApprovalOptionId = {
18156
+ AllowOnce: "allow_once",
18157
+ AllowSession: "allow_session",
18158
+ AllowAlways: "allow_always",
18159
+ Decline: "decline"
18160
+ };
18161
+
18003
18162
  // src/CodexElicitationHandler.ts
18004
18163
  var ELICITATION_OPTIONS = [
18005
18164
  { optionId: "accept", name: "Accept", kind: "allow_once" },
18006
18165
  { optionId: "decline", name: "Decline", kind: "reject_once" }
18007
18166
  ];
18008
- var OPTION_ALLOW_SESSION = "allow_session";
18009
18167
  function parsePersistOptions(meta) {
18010
18168
  const result = /* @__PURE__ */ new Set();
18011
18169
  if (!meta || typeof meta !== "object") return result;
@@ -18025,15 +18183,15 @@ function isMcpToolCallApproval(meta) {
18025
18183
  }
18026
18184
  function buildToolApprovalOptions(persistOptions) {
18027
18185
  const options = [
18028
- { optionId: ApprovalOptionId.AllowOnce, name: "Allow", kind: "allow_once" }
18186
+ { optionId: McpApprovalOptionId.AllowOnce, name: "Allow", kind: "allow_once" }
18029
18187
  ];
18030
18188
  if (persistOptions.has("session")) {
18031
- options.push({ optionId: OPTION_ALLOW_SESSION, name: "Allow for This Session", kind: "allow_always" });
18189
+ options.push({ optionId: McpApprovalOptionId.AllowSession, name: "Allow for This Session", kind: "allow_always" });
18032
18190
  }
18033
18191
  if (persistOptions.has("always")) {
18034
- options.push({ optionId: ApprovalOptionId.AllowAlways, name: "Allow and Don't Ask Again", kind: "allow_always" });
18192
+ options.push({ optionId: McpApprovalOptionId.AllowAlways, name: "Allow and Don't Ask Again", kind: "allow_always" });
18035
18193
  }
18036
- options.push({ optionId: "decline", name: "Decline", kind: "reject_once" });
18194
+ options.push({ optionId: McpApprovalOptionId.Decline, name: "Decline", kind: "reject_once" });
18037
18195
  return options;
18038
18196
  }
18039
18197
  var CodexElicitationHandler = class {
@@ -18080,7 +18238,7 @@ var CodexElicitationHandler = class {
18080
18238
  const response = await this.connection.requestPermission(request);
18081
18239
  if (correlatedCallId !== void 0 && response.outcome.outcome !== "cancelled") {
18082
18240
  const optionId = response.outcome.optionId;
18083
- if (optionId !== "decline") {
18241
+ if (optionId !== McpApprovalOptionId.Decline) {
18084
18242
  await this.connection.sessionUpdate({
18085
18243
  sessionId: this.sessionState.sessionId,
18086
18244
  update: { sessionUpdate: "tool_call_update", toolCallId: correlatedCallId, status: "in_progress" }
@@ -18158,13 +18316,13 @@ var CodexElicitationHandler = class {
18158
18316
  return { action: "cancel", content: null, _meta: null };
18159
18317
  }
18160
18318
  const optionId = response.outcome.optionId;
18161
- if (optionId === OPTION_ALLOW_SESSION) {
18319
+ if (optionId === McpApprovalOptionId.AllowSession) {
18162
18320
  return { action: "accept", content: null, _meta: { persist: "session" } };
18163
18321
  }
18164
- if (optionId === ApprovalOptionId.AllowAlways) {
18322
+ if (optionId === McpApprovalOptionId.AllowAlways) {
18165
18323
  return { action: "accept", content: null, _meta: { persist: "always" } };
18166
18324
  }
18167
- if (optionId === ApprovalOptionId.AllowOnce || optionId === "accept") {
18325
+ if (optionId === McpApprovalOptionId.AllowOnce || optionId === "accept") {
18168
18326
  return { action: "accept", content: null, _meta: null };
18169
18327
  }
18170
18328
  return { action: "decline", content: null, _meta: null };
@@ -18870,6 +19028,7 @@ var ModelId = class _ModelId {
18870
19028
  };
18871
19029
 
18872
19030
  // src/AgentMode.ts
19031
+ var MODE_CONFIG_ID = "mode";
18873
19032
  var AgentMode = class _AgentMode {
18874
19033
  id;
18875
19034
  name;
@@ -18932,6 +19091,21 @@ var AgentMode = class _AgentMode {
18932
19091
  currentModeId: this.id
18933
19092
  };
18934
19093
  }
19094
+ toConfigOption() {
19095
+ return {
19096
+ id: MODE_CONFIG_ID,
19097
+ name: "Mode",
19098
+ description: "Approval and sandboxing preset for the session",
19099
+ category: "mode",
19100
+ type: "select",
19101
+ currentValue: this.id,
19102
+ options: _AgentMode.all().map((mode) => ({
19103
+ value: mode.id,
19104
+ name: mode.name,
19105
+ description: mode.description
19106
+ }))
19107
+ };
19108
+ }
18935
19109
  static all() {
18936
19110
  return [_AgentMode.ReadOnly, _AgentMode.Agent, _AgentMode.AgentFullAccess];
18937
19111
  }
@@ -18952,13 +19126,19 @@ var AgentMode = class _AgentMode {
18952
19126
  // src/CodexAcpClient.ts
18953
19127
  import path4 from "node:path";
18954
19128
 
19129
+ // src/McpServerName.ts
19130
+ var MCP_SERVER_NAME_WHITESPACE = /\p{White_Space}/gu;
19131
+ function sanitizeMcpServerName(name) {
19132
+ return name.replace(MCP_SERVER_NAME_WHITESPACE, "_");
19133
+ }
19134
+
18955
19135
  // package.json
18956
19136
  var package_default = {
18957
19137
  name: "@agentclientprotocol/codex-acp",
18958
19138
  publishConfig: {
18959
19139
  access: "public"
18960
19140
  },
18961
- version: "0.0.44",
19141
+ version: "0.0.46",
18962
19142
  description: "",
18963
19143
  main: "dist/index.js",
18964
19144
  bin: {
@@ -19016,8 +19196,8 @@ var package_default = {
19016
19196
  vitest: "^4.0.10"
19017
19197
  },
19018
19198
  dependencies: {
19019
- "@agentclientprotocol/sdk": "^0.21.0",
19020
- "@openai/codex": "^0.128.0",
19199
+ "@agentclientprotocol/sdk": "^0.22.1",
19200
+ "@openai/codex": "^0.137.0",
19021
19201
  diff: "^8.0.3",
19022
19202
  open: "^11.0.0",
19023
19203
  "vscode-jsonrpc": "^8.2.1"
@@ -19032,6 +19212,7 @@ var CodexAcpClient = class {
19032
19212
  gatewayConfig;
19033
19213
  pendingLoginCompleted = null;
19034
19214
  pendingAccountUpdated = null;
19215
+ sessionNotificationQueues = /* @__PURE__ */ new Map();
19035
19216
  constructor(codexClient, codexConfig, modelProvider) {
19036
19217
  this.codexClient = codexClient;
19037
19218
  this.config = codexConfig ?? {};
@@ -19157,47 +19338,39 @@ var CodexAcpClient = class {
19157
19338
  async getAccount() {
19158
19339
  return this.codexClient.accountRead({ refreshToken: false });
19159
19340
  }
19160
- async resumeSession(request) {
19341
+ async resumeSession(request, onSubscribed) {
19161
19342
  await this.refreshSkills(request.cwd, request._meta);
19162
19343
  const response = await this.codexClient.threadResume({
19163
- approvalPolicy: null,
19164
- sandbox: null,
19165
- baseInstructions: null,
19166
19344
  config: await this.createSessionConfig(request.cwd, request.mcpServers ?? []),
19167
19345
  cwd: request.cwd,
19168
- developerInstructions: null,
19169
- model: null,
19170
19346
  modelProvider: this.getResumeModelProvider(),
19171
- personality: null,
19172
19347
  threadId: request.sessionId
19173
19348
  });
19349
+ onSubscribed?.();
19174
19350
  const codexModels = await this.fetchAvailableModels();
19175
19351
  const currentModelId = this.createModelId(codexModels, response.model, response.reasoningEffort).toString();
19176
19352
  return {
19177
19353
  sessionId: request.sessionId,
19178
19354
  currentModelId,
19179
- models: codexModels
19355
+ models: codexModels,
19356
+ currentServiceTier: response.serviceTier ?? null
19180
19357
  };
19181
19358
  }
19182
- async loadSession(request) {
19359
+ async loadSession(request, onSubscribed) {
19183
19360
  const response = await this.codexClient.threadResume({
19184
- approvalPolicy: null,
19185
- sandbox: null,
19186
- baseInstructions: null,
19187
19361
  config: await this.createSessionConfig(request.cwd, request.mcpServers ?? []),
19188
19362
  cwd: request.cwd,
19189
- developerInstructions: null,
19190
- model: null,
19191
19363
  modelProvider: this.getResumeModelProvider(),
19192
- personality: null,
19193
19364
  threadId: request.sessionId
19194
19365
  });
19366
+ onSubscribed?.();
19195
19367
  const codexModels = await this.fetchAvailableModels();
19196
19368
  const currentModelId = this.createModelId(codexModels, response.model, response.reasoningEffort).toString();
19197
19369
  return {
19198
19370
  sessionId: request.sessionId,
19199
19371
  currentModelId,
19200
19372
  models: codexModels,
19373
+ currentServiceTier: response.serviceTier ?? null,
19201
19374
  thread: response.thread
19202
19375
  };
19203
19376
  }
@@ -19206,14 +19379,7 @@ var CodexAcpClient = class {
19206
19379
  const response = await this.codexClient.threadStart({
19207
19380
  config: await this.createSessionConfig(request.cwd, request.mcpServers),
19208
19381
  modelProvider: this.getModelProvider(),
19209
- model: null,
19210
- cwd: request.cwd,
19211
- approvalPolicy: null,
19212
- sandbox: null,
19213
- baseInstructions: null,
19214
- developerInstructions: null,
19215
- personality: null,
19216
- ephemeral: null
19382
+ cwd: request.cwd
19217
19383
  });
19218
19384
  const codexModels = await this.fetchAvailableModels();
19219
19385
  if (codexModels.length === 0) {
@@ -19223,9 +19389,17 @@ var CodexAcpClient = class {
19223
19389
  return {
19224
19390
  sessionId: response.thread.id,
19225
19391
  currentModelId,
19226
- models: codexModels
19392
+ models: codexModels,
19393
+ currentServiceTier: response.serviceTier ?? null
19227
19394
  };
19228
19395
  }
19396
+ async closeSession(sessionId) {
19397
+ try {
19398
+ await this.codexClient.threadUnsubscribe({ threadId: sessionId });
19399
+ } finally {
19400
+ this.codexClient.clearThreadHandlers(sessionId);
19401
+ }
19402
+ }
19229
19403
  async awaitMcpServerStartup(serverNames, afterVersion) {
19230
19404
  return await this.codexClient.awaitMcpServerStartup(serverNames, afterVersion);
19231
19405
  }
@@ -19245,13 +19419,17 @@ var CodexAcpClient = class {
19245
19419
  return mergedConfig;
19246
19420
  }
19247
19421
  const existingNames = await this.getConfigMcpServerNames(projectPath);
19248
- const uniqueServers = mcpServers.filter((mcp) => !existingNames.has(mcp.name));
19422
+ const requestedServers = mcpServers.map((mcp) => ({
19423
+ name: sanitizeMcpServerName(mcp.name),
19424
+ server: mcp
19425
+ }));
19426
+ const uniqueServers = requestedServers.filter((mcp) => !existingNames.has(mcp.name));
19249
19427
  if (uniqueServers.length === 0) {
19250
19428
  return mergedConfig;
19251
19429
  }
19252
19430
  return {
19253
19431
  ...mergedConfig,
19254
- "mcp_servers": Object.fromEntries(uniqueServers.map((mcp) => [mcp.name, this.createMcpSeverConfig(mcp)]))
19432
+ "mcp_servers": Object.fromEntries(uniqueServers.map((mcp) => [mcp.name, this.createMcpSeverConfig(mcp.server)]))
19255
19433
  };
19256
19434
  }
19257
19435
  async getConfigMcpServerNames(projectPath) {
@@ -19272,14 +19450,13 @@ var CodexAcpClient = class {
19272
19450
  if (!cwd) {
19273
19451
  return;
19274
19452
  }
19275
- const additionalRoots = readAdditionalRoots(meta);
19453
+ const additionalRoots = readAdditionalRoots(meta).map((root) => path4.join(root, ".agents", "skills"));
19454
+ if (additionalRoots.length > 0) {
19455
+ await this.codexClient.skillsExtraRootsSet({ extraRoots: additionalRoots });
19456
+ }
19276
19457
  await this.codexClient.listSkills({
19277
19458
  cwds: [cwd],
19278
- forceReload: true,
19279
- perCwdExtraUserRoots: [{
19280
- cwd,
19281
- extraUserRoots: additionalRoots
19282
- }]
19459
+ forceReload: true
19283
19460
  });
19284
19461
  }
19285
19462
  /**
@@ -19287,13 +19464,17 @@ var CodexAcpClient = class {
19287
19464
  */
19288
19465
  createMcpSeverConfig(mcpServer) {
19289
19466
  if ("type" in mcpServer) {
19290
- if (mcpServer.type == "sse") {
19291
- throw RequestError.invalidRequest("Codex doesn't support MCP SSE transport protocol");
19467
+ switch (mcpServer.type) {
19468
+ case "acp":
19469
+ throw RequestError.invalidRequest("Codex doesn't support MCP ACP transport protocol");
19470
+ case "sse":
19471
+ throw RequestError.invalidRequest("Codex doesn't support MCP SSE transport protocol");
19472
+ case "http":
19473
+ return {
19474
+ "url": mcpServer.url,
19475
+ "http_headers": Object.fromEntries(mcpServer.headers.map((h) => [h.name, h.value]))
19476
+ };
19292
19477
  }
19293
- return {
19294
- "url": mcpServer.url,
19295
- "http_headers": Object.fromEntries(mcpServer.headers.map((h) => [h.name, h.value]))
19296
- };
19297
19478
  }
19298
19479
  return {
19299
19480
  "command": mcpServer.command,
@@ -19313,26 +19494,73 @@ var CodexAcpClient = class {
19313
19494
  return ModelId.create(selectedModel.id, reasoningEffort ?? selectedModel.defaultReasoningEffort);
19314
19495
  }
19315
19496
  async subscribeToSessionEvents(sessionId, eventHandler, approvalHandler, elicitationHandler) {
19316
- this.codexClient.onServerNotification(sessionId, eventHandler);
19317
- this.codexClient.onApprovalRequest(sessionId, approvalHandler);
19318
- this.codexClient.onElicitationRequest(sessionId, elicitationHandler);
19497
+ this.codexClient.onServerNotification(sessionId, (event) => {
19498
+ this.enqueueSessionNotification(sessionId, () => eventHandler(event));
19499
+ });
19500
+ this.codexClient.onApprovalRequest(sessionId, {
19501
+ handleCommandExecution: async (params) => {
19502
+ await this.waitForSessionNotifications(sessionId);
19503
+ return await approvalHandler.handleCommandExecution(params);
19504
+ },
19505
+ handleFileChange: async (params) => {
19506
+ await this.waitForSessionNotifications(sessionId);
19507
+ return await approvalHandler.handleFileChange(params);
19508
+ }
19509
+ });
19510
+ this.codexClient.onElicitationRequest(sessionId, {
19511
+ handleElicitation: async (params) => {
19512
+ await this.waitForSessionNotifications(sessionId);
19513
+ return await elicitationHandler.handleElicitation(params);
19514
+ }
19515
+ });
19516
+ }
19517
+ async waitForSessionNotifications(sessionId) {
19518
+ while (true) {
19519
+ const queue = this.sessionNotificationQueues.get(sessionId);
19520
+ if (!queue) return;
19521
+ await queue;
19522
+ }
19523
+ }
19524
+ enqueueSessionNotification(sessionId, operation) {
19525
+ const run = async () => {
19526
+ try {
19527
+ await operation();
19528
+ } catch (error40) {
19529
+ logger.error("Error handling Codex session notification", error40);
19530
+ }
19531
+ };
19532
+ const previous = this.sessionNotificationQueues.get(sessionId);
19533
+ const next = previous ? previous.then(run, run) : run();
19534
+ this.sessionNotificationQueues.set(sessionId, next);
19535
+ void next.finally(() => {
19536
+ if (this.sessionNotificationQueues.get(sessionId) === next) {
19537
+ this.sessionNotificationQueues.delete(sessionId);
19538
+ }
19539
+ });
19319
19540
  }
19320
- async sendPrompt(request, agentMode, modelId, disableSummary, cwd) {
19541
+ async sendPrompt(request, agentMode, modelId, serviceTier, disableSummary, cwd, onTurnStarted, shouldCancel) {
19321
19542
  const input = buildPromptItems(request.prompt);
19322
19543
  const effort = modelId.effort;
19323
19544
  await this.refreshSkills(cwd, request._meta);
19545
+ if (shouldCancel?.()) {
19546
+ return null;
19547
+ }
19324
19548
  return await this.codexClient.runTurn({
19325
- outputSchema: null,
19326
19549
  threadId: request.sessionId,
19327
19550
  input,
19328
19551
  approvalPolicy: agentMode.approvalPolicy,
19329
19552
  sandboxPolicy: agentMode.sandboxPolicy,
19330
19553
  summary: disableSummary ? "none" : null,
19331
- personality: null,
19332
- cwd: null,
19333
19554
  effort,
19334
- model: modelId.model
19335
- });
19555
+ model: modelId.model,
19556
+ serviceTier
19557
+ }, onTurnStarted);
19558
+ }
19559
+ resolveTurnInterrupted(params) {
19560
+ this.codexClient.resolveTurnInterrupted(params.threadId, params.turnId);
19561
+ }
19562
+ markTurnStale(params) {
19563
+ this.codexClient.markTurnStale(params.threadId, params.turnId);
19336
19564
  }
19337
19565
  async listSkills(params) {
19338
19566
  return this.codexClient.listSkills(params ?? {});
@@ -19374,8 +19602,8 @@ var CodexAcpClient = class {
19374
19602
  });
19375
19603
  });
19376
19604
  }
19377
- async listMcpServers(params = { cursor: null, limit: null }) {
19378
- return this.codexClient.listMcpServerStatus(params);
19605
+ async listMcpServers() {
19606
+ return this.codexClient.listMcpServerStatus({});
19379
19607
  }
19380
19608
  async listSessions(request) {
19381
19609
  const sourceKinds = [
@@ -19403,11 +19631,8 @@ var CodexAcpClient = class {
19403
19631
  const modelProviders = preferredProvider ? [preferredProvider] : [];
19404
19632
  const listResponse = await this.codexClient.threadList({
19405
19633
  cursor: request.cursor ?? null,
19406
- limit: null,
19407
- sortKey: null,
19408
19634
  modelProviders,
19409
- sourceKinds,
19410
- archived: null
19635
+ sourceKinds
19411
19636
  });
19412
19637
  if (listResponse.data.length === 0) {
19413
19638
  const diagnostics = await this.runSessionListDiagnostics();
@@ -19416,14 +19641,14 @@ var CodexAcpClient = class {
19416
19641
  let sessions = listResponse.data.map((thread) => ({
19417
19642
  sessionId: thread.id,
19418
19643
  cwd: thread.cwd,
19419
- title: thread.preview || null,
19644
+ title: (thread.name ?? thread.preview) || null,
19420
19645
  updatedAt: new Date(thread.updatedAt * 1e3).toISOString()
19421
19646
  }));
19422
19647
  if (requestedCwd) {
19423
19648
  const filtered = listResponse.data.filter(filterByCwd).map((thread) => ({
19424
19649
  sessionId: thread.id,
19425
19650
  cwd: thread.cwd,
19426
- title: thread.preview || null,
19651
+ title: (thread.name ?? thread.preview) || null,
19427
19652
  updatedAt: new Date(thread.updatedAt * 1e3).toISOString()
19428
19653
  }));
19429
19654
  if (filtered.length > 0 || path4.isAbsolute(requestedCwd)) {
@@ -19455,30 +19680,9 @@ var CodexAcpClient = class {
19455
19680
  }
19456
19681
  async runSessionListDiagnostics() {
19457
19682
  const [allProviders, archivedAllProviders, customGateway] = await Promise.all([
19458
- this.codexClient.threadList({
19459
- cursor: null,
19460
- limit: null,
19461
- sortKey: null,
19462
- modelProviders: [],
19463
- sourceKinds: null,
19464
- archived: null
19465
- }),
19466
- this.codexClient.threadList({
19467
- cursor: null,
19468
- limit: null,
19469
- sortKey: null,
19470
- modelProviders: [],
19471
- sourceKinds: null,
19472
- archived: true
19473
- }),
19474
- this.codexClient.threadList({
19475
- cursor: null,
19476
- limit: null,
19477
- sortKey: null,
19478
- modelProviders: ["custom-gateway"],
19479
- sourceKinds: null,
19480
- archived: null
19481
- })
19683
+ this.codexClient.threadList({}),
19684
+ this.codexClient.threadList({ archived: true }),
19685
+ this.codexClient.threadList({ modelProviders: ["custom-gateway"] })
19482
19686
  ]);
19483
19687
  return {
19484
19688
  allProviders: {
@@ -19557,6 +19761,44 @@ function mergeGatewayConfig(config2, gatewayConfig) {
19557
19761
  }
19558
19762
  }
19559
19763
 
19764
+ // src/ModelConfigOption.ts
19765
+ var MODEL_CONFIG_ID = "model";
19766
+ var REASONING_EFFORT_CONFIG_ID = "reasoning_effort";
19767
+ function findSupportedEffort(options, effort) {
19768
+ if (!effort) return void 0;
19769
+ return options.find((o) => o.reasoningEffort === effort)?.reasoningEffort;
19770
+ }
19771
+ function createModelConfigOption(availableModels, currentBaseModelId) {
19772
+ return {
19773
+ id: MODEL_CONFIG_ID,
19774
+ name: "Model",
19775
+ description: "Model Codex uses for the session",
19776
+ category: "model",
19777
+ type: "select",
19778
+ currentValue: currentBaseModelId,
19779
+ options: availableModels.map((model) => ({
19780
+ value: model.id,
19781
+ name: model.displayName,
19782
+ description: model.description
19783
+ }))
19784
+ };
19785
+ }
19786
+ function createReasoningEffortConfigOption(supportedReasoningEfforts, currentEffort) {
19787
+ return {
19788
+ id: REASONING_EFFORT_CONFIG_ID,
19789
+ name: "Reasoning effort",
19790
+ description: "How much reasoning effort the model should use",
19791
+ category: "thought_level",
19792
+ type: "select",
19793
+ currentValue: currentEffort,
19794
+ options: supportedReasoningEfforts.map((option) => ({
19795
+ value: option.reasoningEffort,
19796
+ name: option.reasoningEffort,
19797
+ description: option.description
19798
+ }))
19799
+ };
19800
+ }
19801
+
19560
19802
  // src/CodexCommands.ts
19561
19803
  var CodexCommands = class {
19562
19804
  connection;
@@ -19583,13 +19825,6 @@ var CodexCommands = class {
19583
19825
  logger.error(`Failed to publish available commands for session ${sessionId}`, err);
19584
19826
  }
19585
19827
  }
19586
- async tryHandle(prompt, sessionState) {
19587
- const command = this.parseCommand(prompt);
19588
- if (command) {
19589
- return this.handleCommand(command, sessionState);
19590
- }
19591
- return false;
19592
- }
19593
19828
  buildAvailableCommands(skillsEntries) {
19594
19829
  const commands = /* @__PURE__ */ new Map();
19595
19830
  for (const builtin of this.getBuiltinCommands()) {
@@ -19636,25 +19871,22 @@ var CodexCommands = class {
19636
19871
  }
19637
19872
  ];
19638
19873
  }
19639
- parseCommand(prompt) {
19640
- if (prompt.length !== 1) return null;
19641
- const [single] = prompt;
19642
- if (!single) return null;
19643
- if (single.type !== "text") return null;
19644
- const trimmed = single.text.trim();
19645
- if (!trimmed.startsWith("/")) return null;
19646
- const commandText = trimmed.slice(1).trim();
19874
+ getCommandName(prompt) {
19875
+ const firstBlock = prompt[0];
19876
+ if (!firstBlock || firstBlock.type != "text") return null;
19877
+ const text = firstBlock.text.trim();
19878
+ if (!text.startsWith("/")) return null;
19879
+ const commandText = text.slice(1).trim();
19647
19880
  if (commandText.length === 0) return null;
19648
- const [name, ...rest] = commandText.split(/\s+/);
19649
- const input = rest.join(" ").trim();
19650
- return {
19651
- name: name.toLowerCase(),
19652
- input: input.length > 0 ? input : null
19653
- };
19881
+ const [name] = commandText.split(/\s+/);
19882
+ return name?.toLowerCase() ?? null;
19654
19883
  }
19655
- async handleCommand(command, sessionState) {
19884
+ async tryHandleCommand(prompt, sessionState) {
19885
+ const commandName = this.getCommandName(prompt);
19886
+ if (commandName === null) return false;
19887
+ if (commandName.startsWith("$")) return false;
19656
19888
  const sessionId = sessionState.sessionId;
19657
- switch (command.name) {
19889
+ switch (commandName) {
19658
19890
  case "status": {
19659
19891
  const session = new ACPSessionConnection(this.connection, sessionId);
19660
19892
  const message = this.buildStatusMessage(sessionState);
@@ -19706,7 +19938,7 @@ var CodexCommands = class {
19706
19938
  return true;
19707
19939
  }
19708
19940
  default:
19709
- await this.sendUnknownCommandMessage(command.name, sessionId);
19941
+ await this.sendUnknownCommandMessage(commandName, sessionId);
19710
19942
  return true;
19711
19943
  }
19712
19944
  }
@@ -19863,22 +20095,84 @@ function isExtMethodRequest(request) {
19863
20095
  return request.method === "authentication/status" || request.method === "authentication/logout";
19864
20096
  }
19865
20097
 
20098
+ // src/FastModeConfig.ts
20099
+ var FAST_MODE_CONFIG_ID = "fast-mode";
20100
+ var FAST_MODE_ON = "on";
20101
+ var FAST_MODE_OFF = "off";
20102
+ var FAST_MODE_DESCRIPTION = "1.5x speed, increased usage";
20103
+ function modelSupportsFast(model) {
20104
+ return model?.additionalSpeedTiers?.includes("fast") ?? false;
20105
+ }
20106
+ function resolveFastServiceTier(fastModeEnabled, currentModelSupportsFast) {
20107
+ return fastModeEnabled && currentModelSupportsFast ? "fast" : null;
20108
+ }
20109
+ function createFastModeConfigOption(fastModeEnabled) {
20110
+ return {
20111
+ id: FAST_MODE_CONFIG_ID,
20112
+ name: "Fast mode",
20113
+ description: FAST_MODE_DESCRIPTION,
20114
+ category: FAST_MODE_CONFIG_ID,
20115
+ type: "select",
20116
+ currentValue: fastModeEnabled ? FAST_MODE_ON : FAST_MODE_OFF,
20117
+ options: [
20118
+ {
20119
+ value: FAST_MODE_OFF,
20120
+ name: "Off",
20121
+ description: "Default speed, normal usage"
20122
+ },
20123
+ {
20124
+ value: FAST_MODE_ON,
20125
+ name: "On",
20126
+ description: FAST_MODE_DESCRIPTION
20127
+ }
20128
+ ]
20129
+ };
20130
+ }
20131
+
20132
+ // src/JBUtils.ts
20133
+ function isJetBrains2026_1Client(clientInfo) {
20134
+ if (!clientInfo) {
20135
+ return false;
20136
+ }
20137
+ const platform2 = clientInfo._meta?.["platform"];
20138
+ const isIntelliJPlatform = platform2 === "intellij";
20139
+ const isJetBrainsClient = clientInfo.name.startsWith("JetBrains");
20140
+ return (isIntelliJPlatform || isJetBrainsClient) && clientInfo.version.startsWith("2026.1");
20141
+ }
20142
+
19866
20143
  // src/CodexAcpServer.ts
19867
- var CodexAcpServer = class {
20144
+ var CodexAcpServer = class _CodexAcpServer {
20145
+ static MODEL_NAME_TOKEN_OVERRIDES = {
20146
+ gpt: "GPT",
20147
+ mini: "Mini",
20148
+ codex: "Codex"
20149
+ };
19868
20150
  codexAcpClient;
19869
20151
  connection;
19870
20152
  defaultAuthRequest;
19871
20153
  getExitCode;
19872
20154
  availableCommands;
20155
+ clientInfo;
19873
20156
  sessions;
19874
20157
  pendingMcpStartupSessions;
20158
+ pendingTurnStarts;
20159
+ activePrompts;
20160
+ closingSessions;
20161
+ sessionGenerations;
20162
+ sessionOpenGenerations;
19875
20163
  constructor(connection, codexAcpClient, defaultAuthRequest, getExitCode) {
19876
20164
  this.sessions = /* @__PURE__ */ new Map();
19877
20165
  this.pendingMcpStartupSessions = /* @__PURE__ */ new Map();
20166
+ this.pendingTurnStarts = /* @__PURE__ */ new Map();
20167
+ this.activePrompts = /* @__PURE__ */ new Map();
20168
+ this.closingSessions = /* @__PURE__ */ new Map();
20169
+ this.sessionGenerations = /* @__PURE__ */ new Map();
20170
+ this.sessionOpenGenerations = /* @__PURE__ */ new Map();
19878
20171
  this.connection = connection;
19879
20172
  this.codexAcpClient = codexAcpClient;
19880
20173
  this.defaultAuthRequest = defaultAuthRequest ?? null;
19881
20174
  this.getExitCode = getExitCode ?? (() => null);
20175
+ this.clientInfo = null;
19882
20176
  this.availableCommands = new CodexCommands(
19883
20177
  connection,
19884
20178
  codexAcpClient,
@@ -19887,22 +20181,31 @@ var CodexAcpServer = class {
19887
20181
  }
19888
20182
  async initialize(_params) {
19889
20183
  logger.log("Initialize request received");
20184
+ this.clientInfo = _params.clientInfo ?? null;
19890
20185
  await this.runWithProcessCheck(() => this.codexAcpClient.initialize(_params));
19891
20186
  return {
19892
20187
  protocolVersion: PROTOCOL_VERSION,
20188
+ agentInfo: {
20189
+ name: package_default.name,
20190
+ title: "Codex",
20191
+ version: package_default.version
20192
+ },
19893
20193
  agentCapabilities: {
19894
20194
  auth: {
19895
20195
  logout: {}
19896
20196
  },
19897
20197
  loadSession: true,
19898
20198
  promptCapabilities: {
20199
+ embeddedContext: true,
19899
20200
  image: true
19900
20201
  },
19901
20202
  sessionCapabilities: {
19902
20203
  resume: {},
19903
- list: {}
20204
+ list: {},
20205
+ close: {}
19904
20206
  },
19905
20207
  mcpCapabilities: {
20208
+ acp: false,
19906
20209
  http: true,
19907
20210
  sse: false
19908
20211
  }
@@ -19941,24 +20244,123 @@ var CodexAcpServer = class {
19941
20244
  }
19942
20245
  }
19943
20246
  async getOrCreateSession(request) {
20247
+ try {
20248
+ return await this.tryCreateSession(request);
20249
+ } catch (e) {
20250
+ const error40 = e instanceof Error ? e : new Error(String(e));
20251
+ await this.handleError(error40);
20252
+ throw e;
20253
+ }
20254
+ }
20255
+ async handleError(e) {
20256
+ if (e.message.includes("log out")) {
20257
+ await this.runWithProcessCheck(() => this.codexAcpClient.logout());
20258
+ throw RequestError.internalError(`${e.message}
20259
+
20260
+ You have been logged out. Please try again.`);
20261
+ }
20262
+ }
20263
+ beginSessionOpen(sessionId) {
20264
+ const generation = this.getSessionGeneration(sessionId);
20265
+ if (this.sessionIsClosing(sessionId)) {
20266
+ throw RequestError.invalidRequest(`Session ${sessionId} is closing`);
20267
+ }
20268
+ this.sessionOpenGenerations.set(sessionId, generation);
20269
+ return generation;
20270
+ }
20271
+ sessionOpenCanInstall(sessionId, generation) {
20272
+ return !this.sessionIsClosing(sessionId) && this.getSessionGeneration(sessionId) === generation;
20273
+ }
20274
+ async cleanupStaleSessionOpen(sessionId, generation) {
20275
+ if (this.sessionOpenGenerations.get(sessionId) === generation) {
20276
+ if (!this.sessionIsClosing(sessionId)) {
20277
+ this.bumpSessionGeneration(sessionId);
20278
+ }
20279
+ this.beginSessionCloseFence(sessionId);
20280
+ try {
20281
+ await this.runWithProcessCheck(() => this.codexAcpClient.closeSession(sessionId));
20282
+ } catch (err) {
20283
+ logger.error(`Failed to close stale session open for ${sessionId}`, err);
20284
+ } finally {
20285
+ this.endSessionCloseFence(sessionId);
20286
+ }
20287
+ return true;
20288
+ }
20289
+ return false;
20290
+ }
20291
+ async closeStaleSessionOpen(sessionId, generation) {
20292
+ await this.cleanupStaleSessionOpen(sessionId, generation);
20293
+ throw RequestError.invalidRequest(`Session ${sessionId} is closing`);
20294
+ }
20295
+ sessionIsClosing(sessionId) {
20296
+ return (this.closingSessions.get(sessionId) ?? 0) > 0;
20297
+ }
20298
+ beginSessionCloseFence(sessionId) {
20299
+ this.closingSessions.set(sessionId, (this.closingSessions.get(sessionId) ?? 0) + 1);
20300
+ }
20301
+ endSessionCloseFence(sessionId) {
20302
+ const count = this.closingSessions.get(sessionId) ?? 0;
20303
+ if (count <= 1) {
20304
+ this.closingSessions.delete(sessionId);
20305
+ return;
20306
+ }
20307
+ this.closingSessions.set(sessionId, count - 1);
20308
+ }
20309
+ getSessionGeneration(sessionId) {
20310
+ return this.sessionGenerations.get(sessionId) ?? 0;
20311
+ }
20312
+ bumpSessionGeneration(sessionId) {
20313
+ const generation = this.getSessionGeneration(sessionId) + 1;
20314
+ this.sessionGenerations.set(sessionId, generation);
20315
+ return generation;
20316
+ }
20317
+ async tryCreateSession(request) {
20318
+ const requestedSessionGeneration = "sessionId" in request ? this.beginSessionOpen(request.sessionId) : null;
19944
20319
  await this.checkAuthorization();
19945
20320
  const requestedMcpServers = request.mcpServers ?? [];
19946
20321
  const mcpServerStartupVersion = requestedMcpServers.length > 0 ? this.codexAcpClient.getMcpServerStartupVersion() : null;
19947
20322
  let sessionMetadata;
20323
+ let resumeSubscribed = false;
19948
20324
  if ("sessionId" in request) {
19949
20325
  logger.log(`Resume existing session: ${request.sessionId}...`);
19950
- sessionMetadata = await this.runWithProcessCheck(() => this.codexAcpClient.resumeSession(request));
20326
+ try {
20327
+ sessionMetadata = await this.runWithProcessCheck(
20328
+ () => this.codexAcpClient.resumeSession(request, () => {
20329
+ resumeSubscribed = true;
20330
+ })
20331
+ );
20332
+ } catch (err) {
20333
+ if (resumeSubscribed && requestedSessionGeneration !== null) {
20334
+ await this.cleanupStaleSessionOpen(request.sessionId, requestedSessionGeneration);
20335
+ }
20336
+ throw err;
20337
+ }
19951
20338
  } else {
19952
20339
  logger.log(`Create new session...`);
19953
20340
  sessionMetadata = await this.runWithProcessCheck(() => this.codexAcpClient.newSession(request));
19954
20341
  }
19955
- const account = await this.getActiveAccount();
19956
20342
  const { sessionId, currentModelId, models } = sessionMetadata;
20343
+ let account;
20344
+ try {
20345
+ account = await this.getActiveAccount();
20346
+ } catch (err) {
20347
+ if (resumeSubscribed && requestedSessionGeneration !== null) {
20348
+ await this.cleanupStaleSessionOpen(sessionId, requestedSessionGeneration);
20349
+ }
20350
+ throw err;
20351
+ }
20352
+ const sessionGeneration = requestedSessionGeneration ?? this.beginSessionOpen(sessionId);
20353
+ if (!this.sessionOpenCanInstall(sessionId, sessionGeneration)) {
20354
+ resumeSubscribed = false;
20355
+ await this.closeStaleSessionOpen(sessionId, sessionGeneration);
20356
+ }
19957
20357
  const sessionMcpServers = this.resolveSessionMcpServers(requestedMcpServers, "sessionId" in request);
19958
20358
  const currentModel = this.findCurrentModel(models, currentModelId);
20359
+ const currentModelSupportsFast = modelSupportsFast(currentModel);
19959
20360
  const sessionState = {
19960
20361
  sessionId,
19961
20362
  currentModelId,
20363
+ availableModels: models,
19962
20364
  supportedReasoningEfforts: currentModel?.supportedReasoningEfforts ?? [],
19963
20365
  supportedInputModalities: currentModel?.inputModalities ?? ["text", "image"],
19964
20366
  agentMode: AgentMode.getInitialAgentMode(),
@@ -19969,12 +20371,15 @@ var CodexAcpServer = class {
19969
20371
  rateLimits: null,
19970
20372
  account,
19971
20373
  cwd: request.cwd,
20374
+ fastModeEnabled: sessionMetadata.currentServiceTier === "fast",
20375
+ currentModelSupportsFast,
19972
20376
  sessionMcpServers
19973
20377
  };
19974
20378
  this.sessions.set(sessionId, sessionState);
20379
+ resumeSubscribed = false;
19975
20380
  if (requestedMcpServers.length > 0 && mcpServerStartupVersion !== null) {
19976
20381
  this.pendingMcpStartupSessions.set(sessionId, {
19977
- requestedServers: new Set(requestedMcpServers.map((server) => server.name)),
20382
+ requestedServers: new Set(getRequestedMcpServerNames(requestedMcpServers)),
19978
20383
  afterVersion: mcpServerStartupVersion
19979
20384
  });
19980
20385
  this.publishMcpStartupStatusAsync(sessionId);
@@ -20007,7 +20412,8 @@ var CodexAcpServer = class {
20007
20412
  });
20008
20413
  return {
20009
20414
  models: modelState,
20010
- modes: modeState
20415
+ modes: modeState,
20416
+ ...this.createSessionConfigOptionsResponse(this.getSessionState(sessionId))
20011
20417
  };
20012
20418
  }
20013
20419
  async resumeSession(params) {
@@ -20020,7 +20426,8 @@ var CodexAcpServer = class {
20020
20426
  });
20021
20427
  return {
20022
20428
  models: modelState,
20023
- modes: modeState
20429
+ modes: modeState,
20430
+ ...this.createSessionConfigOptionsResponse(this.getSessionState(sessionId))
20024
20431
  };
20025
20432
  }
20026
20433
  async listSessions(params) {
@@ -20028,6 +20435,35 @@ var CodexAcpServer = class {
20028
20435
  await this.checkAuthorization();
20029
20436
  return await this.runWithProcessCheck(() => this.codexAcpClient.listSessions(params));
20030
20437
  }
20438
+ async closeSession(params) {
20439
+ logger.log("Closing session...", { sessionId: params.sessionId });
20440
+ const closeGeneration = this.bumpSessionGeneration(params.sessionId);
20441
+ const sessionState = this.sessions.get(params.sessionId);
20442
+ this.beginSessionCloseFence(params.sessionId);
20443
+ try {
20444
+ if (sessionState) {
20445
+ await this.interruptSessionTurn(sessionState, "Close", true);
20446
+ } else {
20447
+ logger.log("Close request received for unknown local session", { sessionId: params.sessionId });
20448
+ }
20449
+ const activePrompt = this.activePrompts.get(params.sessionId);
20450
+ if (activePrompt) {
20451
+ activePrompt.requestClose();
20452
+ await activePrompt.completion;
20453
+ }
20454
+ await this.runWithProcessCheck(() => this.codexAcpClient.closeSession(params.sessionId));
20455
+ logger.log("Session closed", { sessionId: params.sessionId });
20456
+ } finally {
20457
+ if (this.getSessionGeneration(params.sessionId) === closeGeneration) {
20458
+ this.sessions.delete(params.sessionId);
20459
+ this.pendingMcpStartupSessions.delete(params.sessionId);
20460
+ this.pendingTurnStarts.delete(params.sessionId);
20461
+ this.activePrompts.delete(params.sessionId);
20462
+ }
20463
+ this.endSessionCloseFence(params.sessionId);
20464
+ }
20465
+ return {};
20466
+ }
20031
20467
  async newSession(params) {
20032
20468
  logger.log("Starting new session...");
20033
20469
  const [sessionId, modelState, modeState] = await this.getOrCreateSession(params);
@@ -20039,7 +20475,8 @@ var CodexAcpServer = class {
20039
20475
  return {
20040
20476
  sessionId,
20041
20477
  models: modelState,
20042
- modes: modeState
20478
+ modes: modeState,
20479
+ ...this.createSessionConfigOptionsResponse(this.getSessionState(sessionId))
20043
20480
  };
20044
20481
  }
20045
20482
  async authenticate(_params) {
@@ -20064,12 +20501,75 @@ var CodexAcpServer = class {
20064
20501
  });
20065
20502
  const sessionState = this.sessions.get(_params.sessionId);
20066
20503
  if (!sessionState) throw new Error(`Session ${_params.sessionId} not found`);
20067
- const newMode = AgentMode.find(_params.modeId);
20504
+ this.applyModeChange(sessionState, _params.modeId);
20505
+ return {};
20506
+ }
20507
+ async setSessionConfigOption(params) {
20508
+ logger.log("Set session config option requested", {
20509
+ sessionId: params.sessionId,
20510
+ configId: params.configId
20511
+ });
20512
+ const sessionState = this.sessions.get(params.sessionId);
20513
+ if (!sessionState) throw new Error(`Session ${params.sessionId} not found`);
20514
+ if (typeof params.value !== "string") {
20515
+ throw RequestError.invalidParams();
20516
+ }
20517
+ const value = params.value;
20518
+ switch (params.configId) {
20519
+ case FAST_MODE_CONFIG_ID:
20520
+ this.applyFastModeChange(sessionState, value);
20521
+ break;
20522
+ case MODE_CONFIG_ID:
20523
+ this.applyModeChange(sessionState, value);
20524
+ break;
20525
+ case MODEL_CONFIG_ID:
20526
+ this.applyModelChange(sessionState, value);
20527
+ break;
20528
+ case REASONING_EFFORT_CONFIG_ID:
20529
+ this.applyReasoningEffortChange(sessionState, value);
20530
+ break;
20531
+ default:
20532
+ throw RequestError.invalidParams();
20533
+ }
20534
+ return {
20535
+ configOptions: this.createSessionConfigOptions(sessionState)
20536
+ };
20537
+ }
20538
+ applyFastModeChange(sessionState, value) {
20539
+ if (value !== FAST_MODE_ON && value !== FAST_MODE_OFF) {
20540
+ throw RequestError.invalidParams();
20541
+ }
20542
+ sessionState.fastModeEnabled = value === FAST_MODE_ON;
20543
+ }
20544
+ applyModeChange(sessionState, value) {
20545
+ const newMode = AgentMode.find(value);
20068
20546
  if (!newMode) {
20069
20547
  throw RequestError.invalidParams();
20070
20548
  }
20071
20549
  sessionState.agentMode = newMode;
20072
- return {};
20550
+ }
20551
+ applyModelChange(sessionState, value) {
20552
+ const model = sessionState.availableModels.find((m) => m.id === value);
20553
+ if (!model) {
20554
+ throw RequestError.invalidParams();
20555
+ }
20556
+ const currentEffort = ModelId.fromString(sessionState.currentModelId).effort;
20557
+ const effort = findSupportedEffort(model.supportedReasoningEfforts, currentEffort) ?? model.defaultReasoningEffort;
20558
+ this.applyModelAndEffort(sessionState, model, effort);
20559
+ }
20560
+ applyReasoningEffortChange(sessionState, value) {
20561
+ const effort = findSupportedEffort(sessionState.supportedReasoningEfforts, value);
20562
+ if (!effort) {
20563
+ throw RequestError.invalidParams();
20564
+ }
20565
+ const { model } = ModelId.fromString(sessionState.currentModelId);
20566
+ sessionState.currentModelId = ModelId.create(model, effort).toString();
20567
+ }
20568
+ applyModelAndEffort(sessionState, model, effort) {
20569
+ sessionState.currentModelId = ModelId.fromComponents(model, effort).toString();
20570
+ sessionState.supportedReasoningEfforts = model.supportedReasoningEfforts;
20571
+ sessionState.supportedInputModalities = model.inputModalities;
20572
+ sessionState.currentModelSupportsFast = modelSupportsFast(model);
20073
20573
  }
20074
20574
  async unstable_setSessionModel(params) {
20075
20575
  logger.log("Set session model requested", {
@@ -20078,30 +20578,44 @@ var CodexAcpServer = class {
20078
20578
  });
20079
20579
  const sessionState = this.sessions.get(params.sessionId);
20080
20580
  if (!sessionState) throw new Error(`Session ${params.sessionId} not found`);
20081
- const requestedModelId = ModelId.fromString(params.modelId);
20082
- const requestedModelName = requestedModelId.model;
20083
- const requestedEffort = requestedModelId.effort;
20581
+ const { model: requestedModelName, effort: requestedEffort } = ModelId.fromString(params.modelId);
20084
20582
  const models = await this.codexAcpClient.fetchAvailableModels();
20085
20583
  const model = models.find((m) => m.id === requestedModelName);
20086
20584
  if (!model) throw new Error(`Unknown model ${params.modelId}`);
20087
- const requestedEffortValue = requestedEffort;
20088
20585
  let reasoningEffort;
20089
- if (requestedEffortValue) {
20090
- const matchedEffort = model.supportedReasoningEfforts.find(
20091
- (option) => option.reasoningEffort === requestedEffortValue
20092
- )?.reasoningEffort;
20586
+ if (requestedEffort) {
20587
+ const matchedEffort = findSupportedEffort(model.supportedReasoningEfforts, requestedEffort);
20093
20588
  if (!matchedEffort) {
20094
- throw new Error(`Unsupported reasoning effort ${requestedEffortValue} for model ${requestedModelName}`);
20589
+ throw new Error(`Unsupported reasoning effort ${requestedEffort} for model ${requestedModelName}`);
20095
20590
  }
20096
20591
  reasoningEffort = matchedEffort;
20097
20592
  } else {
20098
20593
  reasoningEffort = model.defaultReasoningEffort;
20099
20594
  }
20100
- sessionState.currentModelId = ModelId.fromComponents(model, reasoningEffort).toString();
20101
- sessionState.supportedReasoningEfforts = model.supportedReasoningEfforts;
20102
- sessionState.supportedInputModalities = model.inputModalities;
20595
+ sessionState.availableModels = models;
20596
+ this.applyModelAndEffort(sessionState, model, reasoningEffort);
20103
20597
  return {};
20104
20598
  }
20599
+ createSessionConfigOptions(sessionState) {
20600
+ const currentModelId = ModelId.fromString(sessionState.currentModelId);
20601
+ return [
20602
+ sessionState.agentMode.toConfigOption(),
20603
+ createModelConfigOption(sessionState.availableModels, currentModelId.model),
20604
+ createReasoningEffortConfigOption(sessionState.supportedReasoningEfforts, currentModelId.effort),
20605
+ createFastModeConfigOption(sessionState.fastModeEnabled)
20606
+ ];
20607
+ }
20608
+ createSessionConfigOptionsResponse(sessionState) {
20609
+ if (!this.isSessionConfigEnabled()) {
20610
+ return {};
20611
+ }
20612
+ return {
20613
+ configOptions: this.createSessionConfigOptions(sessionState)
20614
+ };
20615
+ }
20616
+ isSessionConfigEnabled() {
20617
+ return !isJetBrains2026_1Client(this.clientInfo);
20618
+ }
20105
20619
  publishAvailableCommandsAsync(sessionId) {
20106
20620
  void this.availableCommands.publish(sessionId);
20107
20621
  }
@@ -20109,11 +20623,14 @@ var CodexAcpServer = class {
20109
20623
  const modelId = ModelId.fromString(currentModelId);
20110
20624
  return models.find((m) => m.id === modelId.model);
20111
20625
  }
20626
+ normalizeModelDisplayName(displayName) {
20627
+ return displayName.split("-").map((token) => _CodexAcpServer.MODEL_NAME_TOKEN_OVERRIDES[token.toLowerCase()] ?? token).join("-");
20628
+ }
20112
20629
  createModelState(availableModels, selectedModelId) {
20113
20630
  const allowedModels = availableModels.flatMap(
20114
20631
  (model) => model.supportedReasoningEfforts.map((effort) => ({
20115
20632
  modelId: ModelId.fromComponents(model, effort.reasoningEffort).toString(),
20116
- name: `${model.displayName} (${effort.reasoningEffort})`,
20633
+ name: `${this.normalizeModelDisplayName(model.displayName)} (${effort.reasoningEffort})`,
20117
20634
  description: `${model.description} ${effort.description}`
20118
20635
  }))
20119
20636
  );
@@ -20123,20 +20640,46 @@ var CodexAcpServer = class {
20123
20640
  };
20124
20641
  }
20125
20642
  async getOrCreateSessionWithHistory(request) {
20643
+ const requestedSessionGeneration = this.beginSessionOpen(request.sessionId);
20126
20644
  await this.checkAuthorization();
20127
20645
  const requestedMcpServers = request.mcpServers ?? [];
20128
20646
  const mcpServerStartupVersion = requestedMcpServers.length > 0 ? this.codexAcpClient.getMcpServerStartupVersion() : null;
20129
20647
  logger.log(`Load existing session: ${request.sessionId}...`);
20130
- const sessionMetadata = await this.runWithProcessCheck(
20131
- () => this.codexAcpClient.loadSession(request)
20132
- );
20133
- const account = await this.getActiveAccount();
20648
+ let subscribed = false;
20649
+ let sessionMetadata;
20650
+ try {
20651
+ sessionMetadata = await this.runWithProcessCheck(
20652
+ () => this.codexAcpClient.loadSession(request, () => {
20653
+ subscribed = true;
20654
+ })
20655
+ );
20656
+ } catch (err) {
20657
+ if (subscribed) {
20658
+ await this.cleanupStaleSessionOpen(request.sessionId, requestedSessionGeneration);
20659
+ }
20660
+ throw err;
20661
+ }
20134
20662
  const { sessionId, currentModelId, models, thread } = sessionMetadata;
20663
+ let account;
20664
+ try {
20665
+ account = await this.getActiveAccount();
20666
+ } catch (err) {
20667
+ if (subscribed) {
20668
+ await this.cleanupStaleSessionOpen(request.sessionId, requestedSessionGeneration);
20669
+ }
20670
+ throw err;
20671
+ }
20672
+ if (!this.sessionOpenCanInstall(sessionId, requestedSessionGeneration)) {
20673
+ subscribed = false;
20674
+ await this.closeStaleSessionOpen(sessionId, requestedSessionGeneration);
20675
+ }
20135
20676
  const sessionMcpServers = this.resolveSessionMcpServers(requestedMcpServers, true);
20136
20677
  const currentModel = this.findCurrentModel(models, currentModelId);
20678
+ const currentModelSupportsFast = modelSupportsFast(currentModel);
20137
20679
  const sessionState = {
20138
20680
  sessionId,
20139
20681
  currentModelId,
20682
+ availableModels: models,
20140
20683
  supportedReasoningEfforts: currentModel?.supportedReasoningEfforts ?? [],
20141
20684
  supportedInputModalities: currentModel?.inputModalities ?? ["text", "image"],
20142
20685
  agentMode: AgentMode.getInitialAgentMode(),
@@ -20147,12 +20690,15 @@ var CodexAcpServer = class {
20147
20690
  rateLimits: null,
20148
20691
  account,
20149
20692
  cwd: request.cwd,
20693
+ fastModeEnabled: sessionMetadata.currentServiceTier === "fast",
20694
+ currentModelSupportsFast,
20150
20695
  sessionMcpServers
20151
20696
  };
20152
20697
  this.sessions.set(sessionId, sessionState);
20698
+ subscribed = false;
20153
20699
  if (requestedMcpServers.length > 0 && mcpServerStartupVersion !== null) {
20154
20700
  this.pendingMcpStartupSessions.set(sessionId, {
20155
- requestedServers: new Set(requestedMcpServers.map((server) => server.name)),
20701
+ requestedServers: new Set(getRequestedMcpServerNames(requestedMcpServers)),
20156
20702
  afterVersion: mcpServerStartupVersion
20157
20703
  });
20158
20704
  this.publishMcpStartupStatusAsync(sessionId);
@@ -20398,11 +20944,16 @@ ${item.text}`
20398
20944
  pendingStartup.afterVersion
20399
20945
  )
20400
20946
  );
20947
+ if (!this.sessions.has(sessionId) || this.sessionIsClosing(sessionId) || this.pendingMcpStartupSessions.get(sessionId) !== pendingStartup) {
20948
+ return;
20949
+ }
20401
20950
  await this.publishMcpStartupStatus(sessionId, mcpStartup, pendingStartup.requestedServers);
20402
20951
  } catch (err) {
20403
20952
  logger.error(`Failed to publish MCP startup status for session ${sessionId}`, err);
20404
20953
  } finally {
20405
- this.pendingMcpStartupSessions.delete(sessionId);
20954
+ if (this.pendingMcpStartupSessions.get(sessionId) === pendingStartup) {
20955
+ this.pendingMcpStartupSessions.delete(sessionId);
20956
+ }
20406
20957
  }
20407
20958
  }
20408
20959
  async publishMcpStartupStatus(sessionId, mcpStartup, requestedServers) {
@@ -20418,6 +20969,125 @@ ${item.text}`
20418
20969
  });
20419
20970
  }
20420
20971
  }
20972
+ trackActivePrompt(sessionId) {
20973
+ let resolveCompletion = () => {
20974
+ };
20975
+ const completion = new Promise((resolve) => {
20976
+ resolveCompletion = resolve;
20977
+ });
20978
+ let resolveCloseSignal = () => {
20979
+ };
20980
+ const closeSignal = new Promise((resolve) => {
20981
+ resolveCloseSignal = resolve;
20982
+ });
20983
+ let completed = false;
20984
+ let closeRequested = false;
20985
+ const activePrompt = {
20986
+ completion,
20987
+ closeSignal,
20988
+ requestClose: () => {
20989
+ if (closeRequested) {
20990
+ return;
20991
+ }
20992
+ closeRequested = true;
20993
+ resolveCloseSignal(null);
20994
+ },
20995
+ complete: () => {
20996
+ if (completed) {
20997
+ return;
20998
+ }
20999
+ completed = true;
21000
+ if (this.activePrompts.get(sessionId) === activePrompt) {
21001
+ this.activePrompts.delete(sessionId);
21002
+ }
21003
+ resolveCompletion();
21004
+ }
21005
+ };
21006
+ this.activePrompts.set(sessionId, activePrompt);
21007
+ return activePrompt;
21008
+ }
21009
+ createPendingTurnStart() {
21010
+ let resolve = () => {
21011
+ };
21012
+ const promise2 = new Promise((innerResolve) => {
21013
+ resolve = innerResolve;
21014
+ });
21015
+ return { promise: promise2, resolve };
21016
+ }
21017
+ interruptLateStartedTurn(sessionId, turnId) {
21018
+ this.codexAcpClient.markTurnStale({
21019
+ threadId: sessionId,
21020
+ turnId
21021
+ });
21022
+ void this.runWithProcessCheck(() => this.codexAcpClient.turnInterrupt({
21023
+ threadId: sessionId,
21024
+ turnId
21025
+ })).catch((err) => {
21026
+ logger.error(`Close - late turnInterrupt failed`, err);
21027
+ }).finally(() => {
21028
+ this.codexAcpClient.resolveTurnInterrupted({
21029
+ threadId: sessionId,
21030
+ turnId
21031
+ });
21032
+ });
21033
+ }
21034
+ promptIsClosedOrStale(sessionId, activePrompt) {
21035
+ return this.activePrompts.get(sessionId) !== activePrompt || this.sessionIsClosing(sessionId);
21036
+ }
21037
+ async interruptSessionTurn(sessionState, requestName, resolveInterruptedTurn) {
21038
+ const turnId = await this.getInterruptibleTurnId(sessionState, requestName);
21039
+ if (!turnId) {
21040
+ return;
21041
+ }
21042
+ logger.log(`${requestName} session requested`, {
21043
+ sessionId: sessionState.sessionId,
21044
+ currentTurnId: turnId
21045
+ });
21046
+ if (resolveInterruptedTurn) {
21047
+ this.codexAcpClient.markTurnStale({
21048
+ threadId: sessionState.sessionId,
21049
+ turnId
21050
+ });
21051
+ }
21052
+ try {
21053
+ await this.runWithProcessCheck(() => this.codexAcpClient.turnInterrupt({
21054
+ threadId: sessionState.sessionId,
21055
+ turnId
21056
+ }));
21057
+ logger.log(`${requestName} - turnInterrupt succeeded`, {
21058
+ sessionId: sessionState.sessionId,
21059
+ currentTurnId: turnId
21060
+ });
21061
+ } catch (err) {
21062
+ logger.error(`${requestName} - turnInterrupt failed`, err);
21063
+ } finally {
21064
+ if (resolveInterruptedTurn) {
21065
+ this.codexAcpClient.resolveTurnInterrupted({
21066
+ threadId: sessionState.sessionId,
21067
+ turnId
21068
+ });
21069
+ }
21070
+ }
21071
+ }
21072
+ async getInterruptibleTurnId(sessionState, requestName) {
21073
+ if (sessionState.currentTurnId) {
21074
+ return sessionState.currentTurnId;
21075
+ }
21076
+ const pendingTurnStart = this.pendingTurnStarts.get(sessionState.sessionId);
21077
+ if (!pendingTurnStart) {
21078
+ logger.log(`${requestName} request rejected: no current turn`, { sessionId: sessionState.sessionId });
21079
+ return null;
21080
+ }
21081
+ if (requestName === "Close") {
21082
+ pendingTurnStart.resolve(null);
21083
+ return null;
21084
+ }
21085
+ const turnId = await pendingTurnStart.promise;
21086
+ if (!turnId) {
21087
+ logger.log(`${requestName} request rejected: no current turn`, { sessionId: sessionState.sessionId });
21088
+ }
21089
+ return turnId;
21090
+ }
20421
21091
  async prompt(params) {
20422
21092
  logger.log("Prompt received", {
20423
21093
  sessionId: params.sessionId,
@@ -20426,6 +21096,8 @@ ${item.text}`
20426
21096
  const sessionState = this.getSessionState(params.sessionId);
20427
21097
  sessionState.currentTurnId = null;
20428
21098
  sessionState.lastTokenUsage = null;
21099
+ const activePrompt = this.trackActivePrompt(params.sessionId);
21100
+ let pendingTurnStart = null;
20429
21101
  try {
20430
21102
  const eventHandler = new CodexEventHandler(this.connection, sessionState);
20431
21103
  const approvalHandler = new CodexApprovalHandler(this.connection, sessionState);
@@ -20439,7 +21111,7 @@ ${item.text}`
20439
21111
  approvalHandler,
20440
21112
  elicitationHandler
20441
21113
  );
20442
- if (await this.availableCommands.tryHandle(params.prompt, sessionState)) {
21114
+ if (await this.availableCommands.tryHandleCommand(params.prompt, sessionState)) {
20443
21115
  logger.log("Prompt handled by a command");
20444
21116
  return {
20445
21117
  stopReason: "end_turn",
@@ -20447,6 +21119,13 @@ ${item.text}`
20447
21119
  _meta: this.buildQuotaMeta(sessionState)
20448
21120
  };
20449
21121
  }
21122
+ if (this.sessionIsClosing(params.sessionId)) {
21123
+ return {
21124
+ stopReason: "cancelled",
21125
+ usage: this.buildPromptUsage(sessionState.lastTokenUsage),
21126
+ _meta: this.buildQuotaMeta(sessionState)
21127
+ };
21128
+ }
20450
21129
  const modelId = ModelId.fromString(sessionState.currentModelId);
20451
21130
  const modelLacksReasoning = sessionState.supportedReasoningEfforts.length > 0 && sessionState.supportedReasoningEfforts.every((e) => e.reasoningEffort === "none");
20452
21131
  const disableSummary = sessionState.account?.type === "apiKey" || modelLacksReasoning;
@@ -20460,20 +21139,61 @@ ${item.text}`
20460
21139
  throw RequestError.invalidRequest("The current model does not support image input");
20461
21140
  }
20462
21141
  const agentMode = sessionState.agentMode;
20463
- const turnCompleted = await this.runWithProcessCheck(
20464
- () => this.codexAcpClient.sendPrompt(params, agentMode, modelId, disableSummary, sessionState.cwd)
21142
+ const serviceTier = resolveFastServiceTier(
21143
+ sessionState.fastModeEnabled,
21144
+ sessionState.currentModelSupportsFast
20465
21145
  );
21146
+ pendingTurnStart = this.createPendingTurnStart();
21147
+ this.pendingTurnStarts.set(params.sessionId, pendingTurnStart);
21148
+ const sendPromptPromise = this.runWithProcessCheck(
21149
+ () => this.codexAcpClient.sendPrompt(
21150
+ params,
21151
+ agentMode,
21152
+ modelId,
21153
+ serviceTier,
21154
+ disableSummary,
21155
+ sessionState.cwd,
21156
+ (turnId) => {
21157
+ if (this.promptIsClosedOrStale(params.sessionId, activePrompt)) {
21158
+ this.interruptLateStartedTurn(params.sessionId, turnId);
21159
+ return;
21160
+ }
21161
+ sessionState.currentTurnId = turnId;
21162
+ pendingTurnStart?.resolve(turnId);
21163
+ },
21164
+ () => this.promptIsClosedOrStale(params.sessionId, activePrompt)
21165
+ )
21166
+ );
21167
+ void sendPromptPromise.catch((err) => {
21168
+ if (this.activePrompts.get(params.sessionId) !== activePrompt) {
21169
+ logger.error(`Prompt for closed session ${params.sessionId} failed after close`, err);
21170
+ }
21171
+ });
21172
+ const turnCompleted = await Promise.race([
21173
+ sendPromptPromise,
21174
+ activePrompt.closeSignal
21175
+ ]);
21176
+ if (turnCompleted === null) {
21177
+ return {
21178
+ stopReason: "cancelled",
21179
+ usage: this.buildPromptUsage(sessionState.lastTokenUsage),
21180
+ _meta: this.buildQuotaMeta(sessionState)
21181
+ };
21182
+ }
21183
+ await this.codexAcpClient.waitForSessionNotifications(params.sessionId);
20466
21184
  if (turnCompleted.turn.status === "interrupted") {
20467
- await this.connection.sessionUpdate({
20468
- sessionId: params.sessionId,
20469
- update: {
20470
- sessionUpdate: "agent_message_chunk",
20471
- content: {
20472
- type: "text",
20473
- text: "*Conversation interrupted*"
21185
+ if (!this.sessionIsClosing(params.sessionId) && this.sessions.has(params.sessionId)) {
21186
+ await this.connection.sessionUpdate({
21187
+ sessionId: params.sessionId,
21188
+ update: {
21189
+ sessionUpdate: "agent_message_chunk",
21190
+ content: {
21191
+ type: "text",
21192
+ text: "*Conversation interrupted*"
21193
+ }
20474
21194
  }
20475
- }
20476
- });
21195
+ });
21196
+ }
20477
21197
  return {
20478
21198
  stopReason: "cancelled",
20479
21199
  usage: this.buildPromptUsage(sessionState.lastTokenUsage),
@@ -20495,6 +21215,11 @@ ${item.text}`
20495
21215
  } finally {
20496
21216
  logger.log("Prompt completed", { sessionId: params.sessionId });
20497
21217
  sessionState.currentTurnId = null;
21218
+ if (pendingTurnStart !== null && this.pendingTurnStarts.get(params.sessionId) === pendingTurnStart) {
21219
+ this.pendingTurnStarts.delete(params.sessionId);
21220
+ }
21221
+ pendingTurnStart?.resolve(null);
21222
+ activePrompt.complete();
20498
21223
  }
20499
21224
  }
20500
21225
  buildQuotaMeta(sessionState) {
@@ -20535,30 +21260,11 @@ ${item.text}`
20535
21260
  logger.log("Cancel request rejected: session not found", { sessionId: params.sessionId });
20536
21261
  return;
20537
21262
  }
20538
- if (!sessionState.currentTurnId) {
20539
- logger.log("Cancel request rejected: no current turn", { sessionId: params.sessionId });
20540
- return;
20541
- }
20542
- logger.log("Cancel session requested", {
20543
- sessionId: params.sessionId,
20544
- currentTurnId: sessionState.currentTurnId
20545
- });
20546
- try {
20547
- await this.codexAcpClient.turnInterrupt({
20548
- threadId: params.sessionId,
20549
- turnId: sessionState.currentTurnId
20550
- });
20551
- logger.log("Cancel - turnInterrupt succeeded", {
20552
- sessionId: params.sessionId,
20553
- currentTurnId: sessionState.currentTurnId
20554
- });
20555
- } catch (err) {
20556
- logger.error(`Cancel - turnInterrupt failed`, err);
20557
- }
21263
+ await this.interruptSessionTurn(sessionState, "Cancel", false);
20558
21264
  }
20559
21265
  };
20560
21266
  function getRequestedMcpServerNames(mcpServers) {
20561
- return Array.from(new Set(mcpServers.map((server) => server.name)));
21267
+ return Array.from(new Set(mcpServers.map((server) => sanitizeMcpServerName(server.name))));
20562
21268
  }
20563
21269
 
20564
21270
  // src/CodexAppServerClient.ts
@@ -20575,6 +21281,7 @@ var CodexAppServerClient = class {
20575
21281
  mcpServerStartupResolvers = [];
20576
21282
  pendingTurnCompletionResolvers = /* @__PURE__ */ new Map();
20577
21283
  turnCompletionCaptures = /* @__PURE__ */ new Map();
21284
+ staleTurnIds = /* @__PURE__ */ new Map();
20578
21285
  constructor(connection) {
20579
21286
  this.connection = connection;
20580
21287
  this.connection.onUnhandledNotification((data) => {
@@ -20591,12 +21298,26 @@ var CodexAppServerClient = class {
20591
21298
  if (isTurnCompletedNotification(serverNotification)) {
20592
21299
  this.recordTurnCompleted(serverNotification.params);
20593
21300
  }
21301
+ const routing = extractTurnRouting(serverNotification);
21302
+ const staleTurnNotification = this.isStaleTurn(routing.threadId, routing.turnId);
21303
+ if (staleTurnNotification) {
21304
+ if (isTurnCompletedNotification(serverNotification) && routing.threadId !== null && routing.turnId !== null) {
21305
+ this.clearStaleTurn(routing.threadId, routing.turnId);
21306
+ }
21307
+ for (const callback of this.codexEventHandlers) {
21308
+ callback({ eventType: "notification", ...serverNotification });
21309
+ }
21310
+ return;
21311
+ }
20594
21312
  this.notify(serverNotification);
20595
21313
  for (const callback of this.codexEventHandlers) {
20596
21314
  callback({ eventType: "notification", ...serverNotification });
20597
21315
  }
20598
21316
  });
20599
21317
  this.connection.onRequest(CommandExecutionApprovalRequest, async (params) => {
21318
+ if (this.isStaleTurn(params.threadId, params.turnId)) {
21319
+ return { decision: "cancel" };
21320
+ }
20600
21321
  const handler = this.approvalHandlers.get(params.threadId);
20601
21322
  if (!handler) {
20602
21323
  return { decision: "cancel" };
@@ -20604,6 +21325,9 @@ var CodexAppServerClient = class {
20604
21325
  return await handler.handleCommandExecution(params);
20605
21326
  });
20606
21327
  this.connection.onRequest(FileChangeApprovalRequest, async (params) => {
21328
+ if (this.isStaleTurn(params.threadId, params.turnId)) {
21329
+ return { decision: "cancel" };
21330
+ }
20607
21331
  const handler = this.approvalHandlers.get(params.threadId);
20608
21332
  if (!handler) {
20609
21333
  return { decision: "cancel" };
@@ -20611,6 +21335,9 @@ var CodexAppServerClient = class {
20611
21335
  return await handler.handleFileChange(params);
20612
21336
  });
20613
21337
  this.connection.onRequest(McpServerElicitationRequest, async (params) => {
21338
+ if (this.isStaleTurn(params.threadId, params.turnId)) {
21339
+ return { action: "cancel", content: null, _meta: null };
21340
+ }
20614
21341
  const handler = this.elicitationHandlers.get(params.threadId);
20615
21342
  if (!handler) {
20616
21343
  return { action: "cancel", content: null, _meta: null };
@@ -20624,19 +21351,25 @@ var CodexAppServerClient = class {
20624
21351
  onElicitationRequest(threadId, handler) {
20625
21352
  this.elicitationHandlers.set(threadId, handler);
20626
21353
  }
21354
+ clearThreadHandlers(threadId) {
21355
+ this.notificationHandlers.delete(threadId);
21356
+ this.approvalHandlers.delete(threadId);
21357
+ this.elicitationHandlers.delete(threadId);
21358
+ }
20627
21359
  async initialize(params) {
20628
21360
  return await this.sendRequest({ method: "initialize", params });
20629
21361
  }
20630
21362
  async turnStart(params) {
20631
21363
  return await this.sendRequest({ method: "turn/start", params });
20632
21364
  }
20633
- async runTurn(params) {
21365
+ async runTurn(params, onTurnStarted) {
20634
21366
  const capturedCompletions = [];
20635
21367
  const releaseCapture = this.captureTurnCompletions(params.threadId, (event) => {
20636
21368
  capturedCompletions.push(event);
20637
21369
  });
20638
21370
  try {
20639
21371
  const turnStarted = await this.turnStart(params);
21372
+ onTurnStarted?.(turnStarted.turn.id);
20640
21373
  const earlyCompletion = capturedCompletions.find((event) => event.turn.id === turnStarted.turn.id);
20641
21374
  releaseCapture();
20642
21375
  if (earlyCompletion) {
@@ -20650,6 +21383,11 @@ var CodexAppServerClient = class {
20650
21383
  async turnInterrupt(params) {
20651
21384
  return await this.sendRequest({ method: "turn/interrupt", params });
20652
21385
  }
21386
+ markTurnStale(threadId, turnId) {
21387
+ const threadStaleTurns = this.staleTurnIds.get(threadId) ?? /* @__PURE__ */ new Set();
21388
+ threadStaleTurns.add(turnId);
21389
+ this.staleTurnIds.set(threadId, threadStaleTurns);
21390
+ }
20653
21391
  async threadStart(params) {
20654
21392
  return await this.sendRequest({ method: "thread/start", params });
20655
21393
  }
@@ -20665,6 +21403,9 @@ var CodexAppServerClient = class {
20665
21403
  async threadRead(params) {
20666
21404
  return await this.sendRequest({ method: "thread/read", params });
20667
21405
  }
21406
+ async threadUnsubscribe(params) {
21407
+ return await this.sendRequest({ method: "thread/unsubscribe", params });
21408
+ }
20668
21409
  async listMcpServerStatus(params) {
20669
21410
  return await this.sendRequest({ method: "mcpServerStatus/list", params });
20670
21411
  }
@@ -20707,10 +21448,28 @@ var CodexAppServerClient = class {
20707
21448
  threadResolvers.set(turnId, resolve);
20708
21449
  });
20709
21450
  }
20710
- async listModels(params = { cursor: null, limit: null }) {
21451
+ resolveTurnInterrupted(threadId, turnId) {
21452
+ this.recordTurnCompleted({
21453
+ threadId,
21454
+ turn: {
21455
+ id: turnId,
21456
+ items: [],
21457
+ itemsView: "notLoaded",
21458
+ status: "interrupted",
21459
+ error: null,
21460
+ startedAt: null,
21461
+ completedAt: null,
21462
+ durationMs: null
21463
+ }
21464
+ });
21465
+ }
21466
+ async listModels(params) {
20711
21467
  return await this.sendRequest({ method: "model/list", params });
20712
21468
  }
20713
- async listSkills(params = {}) {
21469
+ async skillsExtraRootsSet(params) {
21470
+ return await this.sendRequest({ method: "skills/extraRoots/set", params });
21471
+ }
21472
+ async listSkills(params) {
20714
21473
  return await this.sendRequest({ method: "skills/list", params });
20715
21474
  }
20716
21475
  /**
@@ -20757,6 +21516,22 @@ var CodexAppServerClient = class {
20757
21516
  capture(event);
20758
21517
  }
20759
21518
  }
21519
+ isStaleTurn(threadId, turnId) {
21520
+ if (threadId === null || turnId === null) {
21521
+ return false;
21522
+ }
21523
+ return this.staleTurnIds.get(threadId)?.has(turnId) ?? false;
21524
+ }
21525
+ clearStaleTurn(threadId, turnId) {
21526
+ const threadStaleTurns = this.staleTurnIds.get(threadId);
21527
+ if (!threadStaleTurns) {
21528
+ return;
21529
+ }
21530
+ threadStaleTurns.delete(turnId);
21531
+ if (threadStaleTurns.size === 0) {
21532
+ this.staleTurnIds.delete(threadId);
21533
+ }
21534
+ }
20760
21535
  getOrCreatePendingTurnCompletionResolvers(threadId) {
20761
21536
  const existing = this.pendingTurnCompletionResolvers.get(threadId);
20762
21537
  if (existing) {
@@ -20851,6 +21626,17 @@ function extractThreadId(notification) {
20851
21626
  }
20852
21627
  return null;
20853
21628
  }
21629
+ function extractTurnRouting(notification) {
21630
+ const params = notification.params;
21631
+ const threadId = extractThreadId(notification);
21632
+ if (params && typeof params.turnId === "string") {
21633
+ return { threadId, turnId: params.turnId };
21634
+ }
21635
+ if (params && typeof params.turn?.id === "string") {
21636
+ return { threadId, turnId: params.turn.id };
21637
+ }
21638
+ return { threadId, turnId: null };
21639
+ }
20854
21640
 
20855
21641
  // src/login.ts
20856
21642
  function parseArgs(args) {
@@ -20955,6 +21741,34 @@ async function runLoginCommand(args) {
20955
21741
  return login(options);
20956
21742
  }
20957
21743
 
21744
+ // src/CodexCli.ts
21745
+ import { spawn as spawn2 } from "node:child_process";
21746
+ import { createRequire as createRequire2 } from "node:module";
21747
+ function runCodexCli(codexPath, args) {
21748
+ const child = spawnCodexCli(codexPath, args);
21749
+ return new Promise((resolve, reject) => {
21750
+ child.on("error", reject);
21751
+ child.on("exit", (code, signal) => {
21752
+ if (signal) {
21753
+ process.kill(process.pid, signal);
21754
+ return;
21755
+ }
21756
+ resolve(code ?? 1);
21757
+ });
21758
+ });
21759
+ }
21760
+ function spawnCodexCli(codexPath, args) {
21761
+ const options = {
21762
+ env: process.env,
21763
+ stdio: "inherit"
21764
+ };
21765
+ if (codexPath) {
21766
+ return spawn2(codexPath, args, { ...options, shell: process.platform === "win32" });
21767
+ }
21768
+ const bundledCodexPath = createRequire2(import.meta.url).resolve("@openai/codex/bin/codex.js");
21769
+ return spawn2(process.execPath, [bundledCodexPath, ...args], options);
21770
+ }
21771
+
20958
21772
  // src/index.ts
20959
21773
  if (process.argv.includes("--version")) {
20960
21774
  console.log(`${package_default.name} ${package_default.version}`);
@@ -20966,6 +21780,12 @@ if (process.argv[2] === "login") {
20966
21780
  console.error("Login error:", error40.message);
20967
21781
  process.exit(1);
20968
21782
  });
21783
+ } else if (process.argv[2] === "cli") {
21784
+ const args = process.argv.slice(3);
21785
+ runCodexCli(process.env["CODEX_PATH"], args).then((exitCode) => process.exit(exitCode)).catch((error40) => {
21786
+ console.error("Codex CLI error:", error40.message);
21787
+ process.exit(1);
21788
+ });
20969
21789
  } else {
20970
21790
  startAcpServer();
20971
21791
  }