@agentconnect.md/codex-acp 1.10.0-agentconnect.1 → 1.10.0-agentconnect.2

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 +87 -47
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -25641,14 +25641,16 @@ function unique(values) {
25641
25641
 
25642
25642
  // src/permissions/CodexApprovalHandler.ts
25643
25643
  var CodexApprovalHandler = class {
25644
- constructor(connection, permissionContext, cancellationSignal) {
25644
+ constructor(connection, permissionContext, cancellationSignal, denyRequests) {
25645
25645
  this.connection = connection;
25646
25646
  this.permissionContext = permissionContext;
25647
25647
  this.cancellationSignal = cancellationSignal;
25648
+ this.denyRequests = denyRequests;
25648
25649
  }
25649
25650
  connection;
25650
25651
  permissionContext;
25651
25652
  cancellationSignal;
25653
+ denyRequests;
25652
25654
  async handleCommandExecution(params) {
25653
25655
  const authoritativeParams = params;
25654
25656
  const decisions = commandDecisionOptions(authoritativeParams);
@@ -25707,6 +25709,7 @@ var CodexApprovalHandler = class {
25707
25709
  }
25708
25710
  }
25709
25711
  requestPermission(request) {
25712
+ if (this.denyRequests?.()) return Promise.resolve({ outcome: { outcome: "cancelled" } });
25710
25713
  return this.connection.request(
25711
25714
  methods.client.session.requestPermission,
25712
25715
  request,
@@ -26099,31 +26102,20 @@ function userInputResponseValue(content, fieldId) {
26099
26102
  return value;
26100
26103
  }
26101
26104
  var CodexElicitationHandler = class {
26102
- connection;
26103
- permissionContext;
26104
- clientCapabilities;
26105
- cancellationSignal;
26106
- // In Rust, the MCP elicitation handler receives ElicitationRequestEvent directly from the MCP
26107
- // protocol layer, where id is set to "mcp_tool_call_approval_<call_id>" — the call ID is extracted
26108
- // by stripping that prefix.
26109
- //
26110
- // In TypeScript, Codex speaks the app-server JSON-RPC protocol (v2), where
26111
- // McpServerElicitationRequestParams omits elicitationId for form mode, so the MCP-level ID never
26112
- // reaches the client.
26113
- //
26114
- // Workaround: before requesting approval, Codex emits an item/started notification with an
26115
- // mcpToolCall item carrying the call id and server name. The shared permission lifecycle stores
26116
- // (threadId, serverName) → callId so this request can correlate to the rendered tool call item.
26117
- //
26118
- // The app-server handler exposes URL elicitationId, while serverRequest/resolved only exposes
26119
- // threadId here, so accepted URL elicitations are completed at thread scope.
26120
- pendingUrlElicitations = /* @__PURE__ */ new Map();
26121
- constructor(connection, permissionContext, clientCapabilities = null, cancellationSignal) {
26105
+ constructor(connection, permissionContext, clientCapabilities = null, cancellationSignal, fullAccessHttpServers) {
26106
+ this.fullAccessHttpServers = fullAccessHttpServers;
26122
26107
  this.connection = connection;
26123
26108
  this.permissionContext = permissionContext;
26124
26109
  this.clientCapabilities = clientCapabilities;
26125
26110
  this.cancellationSignal = cancellationSignal;
26126
26111
  }
26112
+ fullAccessHttpServers;
26113
+ connection;
26114
+ permissionContext;
26115
+ clientCapabilities;
26116
+ cancellationSignal;
26117
+ // App-server omits form elicitation IDs, so item/started correlates MCP approvals to tool calls.
26118
+ pendingUrlElicitations = /* @__PURE__ */ new Map();
26127
26119
  async handleNotification(notification) {
26128
26120
  switch (notification.method) {
26129
26121
  case "serverRequest/resolved":
@@ -26136,6 +26128,12 @@ var CodexElicitationHandler = class {
26136
26128
  async handleElicitation(params) {
26137
26129
  try {
26138
26130
  const context = this.createMcpElicitationContext(params);
26131
+ const httpServers = this.fullAccessHttpServers?.(params);
26132
+ if (httpServers !== void 0) {
26133
+ const accepted = !this.cancellationSignal?.aborted && params.mode === "form" && context.isToolApproval && context.persistOptions.has("session") && httpServers.has(params.serverName);
26134
+ await this.publishAcceptedMcpToolApproval(params.threadId, context, accepted);
26135
+ return { action: accepted ? "accept" : "cancel", content: accepted ? {} : null, _meta: null };
26136
+ }
26139
26137
  if (this.shouldUseAcpElicitation(params)) {
26140
26138
  const response2 = await this.connection.request(
26141
26139
  methods.client.elicitation.create,
@@ -26181,6 +26179,9 @@ var CodexElicitationHandler = class {
26181
26179
  }
26182
26180
  }
26183
26181
  async handleUserInput(params) {
26182
+ if (this.fullAccessHttpServers?.(params) !== void 0 && params.itemId === `mcp-deps-${params.turnId}` && params.questions.some((question) => question.id === "skill_mcp_dependency_install")) {
26183
+ return { answers: {} };
26184
+ }
26184
26185
  if (!clientSupportsFormElicitation(this.clientCapabilities)) {
26185
26186
  return { answers: {} };
26186
26187
  }
@@ -27329,7 +27330,7 @@ var package_default = {
27329
27330
  publishConfig: {
27330
27331
  access: "public"
27331
27332
  },
27332
- version: "1.10.0-agentconnect.1",
27333
+ version: "1.10.0-agentconnect.2",
27333
27334
  description: "",
27334
27335
  main: "dist/index.js",
27335
27336
  bin: {
@@ -27897,12 +27898,9 @@ import { createHash } from "node:crypto";
27897
27898
  async function forkSession(request, additionalDirectories, dependencies) {
27898
27899
  await dependencies.refreshSkills(request.cwd, additionalDirectories);
27899
27900
  const lastTurnId = await resolveForkTurnId(request, dependencies.codexClient);
27901
+ const prepared = await dependencies.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers ?? []);
27900
27902
  const response = await dependencies.codexClient.threadFork({
27901
- config: await dependencies.createSessionConfig(
27902
- request.cwd,
27903
- additionalDirectories,
27904
- request.mcpServers ?? []
27905
- ),
27903
+ config: prepared.config,
27906
27904
  cwd: request.cwd,
27907
27905
  ...lastTurnId !== void 0 && { lastTurnId },
27908
27906
  modelProvider: await dependencies.getResumeModelProvider(),
@@ -27917,7 +27915,8 @@ async function forkSession(request, additionalDirectories, dependencies) {
27917
27915
  collaborationMode: dependencies.getCollaborationMode(response.thread.id),
27918
27916
  modelProvider: response.modelProvider,
27919
27917
  currentServiceTier: response.serviceTier ?? null,
27920
- additionalDirectories
27918
+ additionalDirectories,
27919
+ fullAccessHttpMcpServers: prepared.fullAccessHttpMcpServers
27921
27920
  };
27922
27921
  }
27923
27922
  async function resolveForkTurnId(request, codexClient) {
@@ -28378,8 +28377,10 @@ var CodexAcpClient = class {
28378
28377
  const additionalDirectories = readAdditionalDirectories(request.cwd, request.additionalDirectories, request._meta);
28379
28378
  const initialAgentMode = AgentMode.getInitialAgentMode();
28380
28379
  await this.refreshSkills(request.cwd, additionalDirectories);
28380
+ const prepared = await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers ?? []);
28381
+ await this.restrictResumedHttpMcpServers(request.sessionId, prepared);
28381
28382
  const response = await this.codexClient.threadResume({
28382
- config: await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers ?? []),
28383
+ config: prepared.config,
28383
28384
  cwd: request.cwd,
28384
28385
  modelProvider: await this.getResumeModelProvider(),
28385
28386
  threadId: request.sessionId,
@@ -28395,7 +28396,8 @@ var CodexAcpClient = class {
28395
28396
  collaborationMode: this.getCollaborationMode(response.thread.id),
28396
28397
  modelProvider: response.modelProvider,
28397
28398
  currentServiceTier: response.serviceTier ?? null,
28398
- additionalDirectories
28399
+ additionalDirectories,
28400
+ fullAccessHttpMcpServers: prepared.fullAccessHttpMcpServers
28399
28401
  };
28400
28402
  }
28401
28403
  async forkSession(request) {
@@ -28414,8 +28416,10 @@ var CodexAcpClient = class {
28414
28416
  const additionalDirectories = readAdditionalDirectories(request.cwd, request.additionalDirectories, request._meta);
28415
28417
  const initialAgentMode = AgentMode.getInitialAgentMode();
28416
28418
  await this.refreshSkills(request.cwd, additionalDirectories);
28419
+ const prepared = await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers ?? []);
28420
+ await this.restrictResumedHttpMcpServers(request.sessionId, prepared);
28417
28421
  const response = await this.codexClient.threadResume({
28418
- config: await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers ?? []),
28422
+ config: prepared.config,
28419
28423
  cwd: request.cwd,
28420
28424
  modelProvider: await this.getResumeModelProvider(),
28421
28425
  threadId: request.sessionId,
@@ -28436,7 +28440,8 @@ var CodexAcpClient = class {
28436
28440
  modelProvider: response.modelProvider,
28437
28441
  currentServiceTier: response.serviceTier ?? null,
28438
28442
  thread: historyResponse.thread,
28439
- additionalDirectories
28443
+ additionalDirectories,
28444
+ fullAccessHttpMcpServers: prepared.fullAccessHttpMcpServers
28440
28445
  };
28441
28446
  }
28442
28447
  async readSessionThread(sessionId) {
@@ -28449,8 +28454,9 @@ var CodexAcpClient = class {
28449
28454
  const additionalDirectories = readAdditionalDirectories(request.cwd, request.additionalDirectories, request._meta);
28450
28455
  const initialAgentMode = AgentMode.getInitialAgentMode();
28451
28456
  await this.refreshSkills(request.cwd, additionalDirectories);
28457
+ const prepared = await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers);
28452
28458
  const response = await this.codexClient.threadStart({
28453
- config: await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers),
28459
+ config: prepared.config,
28454
28460
  modelProvider: this.getModelProvider(),
28455
28461
  cwd: request.cwd,
28456
28462
  ...this.permissionProfileSelection(initialAgentMode, request.cwd, additionalDirectories)
@@ -28467,7 +28473,8 @@ var CodexAcpClient = class {
28467
28473
  collaborationMode: this.getCollaborationMode(response.thread.id),
28468
28474
  modelProvider: response.modelProvider,
28469
28475
  currentServiceTier: response.serviceTier ?? null,
28470
- additionalDirectories
28476
+ additionalDirectories,
28477
+ fullAccessHttpMcpServers: prepared.fullAccessHttpMcpServers
28471
28478
  };
28472
28479
  }
28473
28480
  async closeSession(sessionId) {
@@ -28482,11 +28489,23 @@ var CodexAcpClient = class {
28482
28489
  const config2 = this.permissionProfileConfig;
28483
28490
  if (!config2) return {};
28484
28491
  return {
28485
- approvalPolicy: agentMode.approvalPolicy,
28492
+ approvalPolicy: this.approvalPolicyForMode(agentMode),
28486
28493
  permissions: permissionProfileForMode(config2, agentMode.id),
28487
28494
  runtimeWorkspaceRoots: sessionRoots(cwd, additionalDirectories)
28488
28495
  };
28489
28496
  }
28497
+ isProtectedFullAccess(agentMode) {
28498
+ return this.permissionProfileConfig !== void 0 && agentMode.kind === "full_access";
28499
+ }
28500
+ approvalPolicyForMode(agentMode) {
28501
+ if (!this.isProtectedFullAccess(agentMode)) return agentMode.approvalPolicy;
28502
+ return { granular: { sandbox_approval: false, rules: false, skill_approval: false, request_permissions: false, mcp_elicitations: false } };
28503
+ }
28504
+ async restrictResumedHttpMcpServers(sessionId, prepared) {
28505
+ if (prepared.fullAccessHttpMcpServers.length === 0) return;
28506
+ const loaded = await this.codexClient.threadLoadedList({});
28507
+ if (loaded.data.includes(sessionId) || loaded.nextCursor !== null) prepared.fullAccessHttpMcpServers = [];
28508
+ }
28490
28509
  async deleteSession(sessionId) {
28491
28510
  await this.codexClient.threadArchive({ threadId: sessionId });
28492
28511
  }
@@ -28571,23 +28590,25 @@ var CodexAcpClient = class {
28571
28590
  };
28572
28591
  const configWithWorkspaceRoots = mergeSandboxWorkspaceWriteRoots(mergedConfig, additionalDirectories);
28573
28592
  if (mcpServers.length === 0) {
28574
- return configWithWorkspaceRoots;
28593
+ return { config: configWithWorkspaceRoots, fullAccessHttpMcpServers: [] };
28575
28594
  }
28576
28595
  const requestedServers = mcpServers.map((mcp) => ({
28577
28596
  name: sanitizeMcpServerName(mcp.name),
28578
28597
  server: mcp
28579
28598
  }));
28580
28599
  let serversToConfigure = requestedServers;
28600
+ const existingNames = shouldDeduplicateMcpConflicts() || this.permissionProfileConfig ? await this.getConfigMcpServerNames(projectPath) : /* @__PURE__ */ new Set();
28581
28601
  if (shouldDeduplicateMcpConflicts()) {
28582
- const existingNames = await this.getConfigMcpServerNames(projectPath);
28583
28602
  serversToConfigure = requestedServers.filter((mcp) => !existingNames.has(mcp.name));
28584
28603
  }
28585
28604
  if (serversToConfigure.length === 0) {
28586
- return configWithWorkspaceRoots;
28605
+ return { config: configWithWorkspaceRoots, fullAccessHttpMcpServers: [] };
28587
28606
  }
28607
+ const configuredServers = Object.fromEntries(serversToConfigure.map((mcp) => [mcp.name, this.createMcpSeverConfig(mcp.server)]));
28608
+ const inheritedServers = isJsonObject(this.config["mcp_servers"]) ? this.config["mcp_servers"] : {};
28588
28609
  return {
28589
- ...configWithWorkspaceRoots,
28590
- "mcp_servers": Object.fromEntries(serversToConfigure.map((mcp) => [mcp.name, this.createMcpSeverConfig(mcp.server)]))
28610
+ config: { ...configWithWorkspaceRoots, mcp_servers: configuredServers },
28611
+ fullAccessHttpMcpServers: this.permissionProfileConfig ? Object.entries(configuredServers).filter(([name, server]) => typeof server["url"] === "string" && !existingNames.has(name) && !Object.hasOwn(inheritedServers, name)).map(([name]) => name) : []
28591
28612
  };
28592
28613
  }
28593
28614
  async getConfigMcpServerNames(projectPath) {
@@ -28720,7 +28741,7 @@ var CodexAcpClient = class {
28720
28741
  return await this.codexClient.runTurn({
28721
28742
  threadId: request.sessionId,
28722
28743
  input,
28723
- approvalPolicy: agentMode.approvalPolicy,
28744
+ approvalPolicy: this.approvalPolicyForMode(agentMode),
28724
28745
  approvalsReviewer: agentMode.approvalsReviewer,
28725
28746
  ...sandboxSelection,
28726
28747
  summary: disableSummary ? "none" : "auto",
@@ -28828,7 +28849,7 @@ var CodexAcpClient = class {
28828
28849
  if (!config2) return;
28829
28850
  await this.codexClient.threadSettingsUpdate({
28830
28851
  threadId: sessionId,
28831
- approvalPolicy: agentMode.approvalPolicy,
28852
+ approvalPolicy: this.approvalPolicyForMode(agentMode),
28832
28853
  permissions: permissionProfileForMode(config2, agentMode.id)
28833
28854
  });
28834
28855
  }
@@ -32594,6 +32615,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
32594
32615
  supportedReasoningEfforts: currentModel?.supportedReasoningEfforts ?? [],
32595
32616
  supportedInputModalities: currentModel?.inputModalities ?? ["text", "image"],
32596
32617
  agentMode: AgentMode.getInitialAgentMode(),
32618
+ fullAccessHttpMcpServers: sessionMetadata.fullAccessHttpMcpServers ?? [],
32597
32619
  collaborationMode: sessionMetadata.collaborationMode,
32598
32620
  currentTurnId: null,
32599
32621
  lastTokenUsage: null,
@@ -32927,12 +32949,13 @@ Check ${configPath} and project .codex directories, especially their config.toml
32927
32949
  for (const session of this.sessions.values()) {
32928
32950
  session.asyncTasks.setAppServer(replacement.appServerClient);
32929
32951
  try {
32930
- await replacement.resumeSession({
32952
+ const metadata = await replacement.resumeSession({
32931
32953
  sessionId: session.sessionId,
32932
32954
  cwd: session.cwd,
32933
32955
  additionalDirectories: session.additionalDirectories,
32934
32956
  mcpServers: session.mcpServers ?? []
32935
32957
  });
32958
+ session.fullAccessHttpMcpServers = metadata.fullAccessHttpMcpServers ?? [];
32936
32959
  session.authProvider = replacement.getModelProvider();
32937
32960
  session.asyncTasks.refresh();
32938
32961
  logger.log("Resumed session after provider restart", { sessionId: session.sessionId });
@@ -33220,6 +33243,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
33220
33243
  if (!newMode) {
33221
33244
  throw RequestError.invalidParams();
33222
33245
  }
33246
+ sessionState.permissionModeRevision = (sessionState.permissionModeRevision ?? 0) + 1;
33223
33247
  await this.codexAcpClient.setAgentMode(
33224
33248
  sessionState.sessionId,
33225
33249
  newMode
@@ -33656,6 +33680,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
33656
33680
  supportedReasoningEfforts: currentModel?.supportedReasoningEfforts ?? [],
33657
33681
  supportedInputModalities: currentModel?.inputModalities ?? ["text", "image"],
33658
33682
  agentMode: AgentMode.getInitialAgentMode(),
33683
+ fullAccessHttpMcpServers: sessionMetadata.fullAccessHttpMcpServers ?? [],
33659
33684
  collaborationMode: sessionMetadata.collaborationMode,
33660
33685
  currentTurnId: null,
33661
33686
  lastTokenUsage: null,
@@ -34400,16 +34425,27 @@ Check ${configPath} and project .codex directories, especially their config.toml
34400
34425
  eventHandler = promptEventHandler;
34401
34426
  const permissionLifecycle = this.permissionLifecycleContext(sessionState);
34402
34427
  const permissionContext = permissionLifecycle.beginPrompt();
34428
+ let promptAgentMode;
34429
+ let permissionModeRevision;
34430
+ const httpServers = new Set(sessionState.fullAccessHttpMcpServers);
34431
+ const noHttpServers = /* @__PURE__ */ new Set();
34432
+ let mcpApprovalTurnId = null;
34433
+ const isProtectedFullAccess = () => this.codexAcpClient.isProtectedFullAccess(promptAgentMode ?? sessionState.agentMode);
34403
34434
  const approvalHandler = new CodexApprovalHandler(
34404
34435
  this.connection,
34405
34436
  permissionContext,
34406
- activePrompt.signal
34437
+ activePrompt.signal,
34438
+ isProtectedFullAccess
34407
34439
  );
34408
34440
  const elicitationHandler = new CodexElicitationHandler(
34409
34441
  this.connection,
34410
34442
  permissionContext,
34411
34443
  this.clientCapabilities,
34412
- activePrompt.signal
34444
+ activePrompt.signal,
34445
+ (request) => {
34446
+ if (!isProtectedFullAccess()) return void 0;
34447
+ return mcpApprovalTurnId !== null && request.turnId === mcpApprovalTurnId && sessionState.agentMode.kind === "full_access" && sessionState.permissionModeRevision === permissionModeRevision ? httpServers : noHttpServers;
34448
+ }
34413
34449
  );
34414
34450
  const observeInteraction = async (event) => {
34415
34451
  permissionContext.handleNotification(event);
@@ -34534,6 +34570,8 @@ Check ${configPath} and project .codex directories, especially their config.toml
34534
34570
  throw RequestError.invalidRequest("The current model does not support image input");
34535
34571
  }
34536
34572
  const agentMode = sessionState.agentMode;
34573
+ promptAgentMode = agentMode;
34574
+ permissionModeRevision = sessionState.permissionModeRevision;
34537
34575
  const serviceTier = resolveFastServiceTier(
34538
34576
  sessionState.fastModeEnabled,
34539
34577
  sessionState.currentModelSupportsFast
@@ -34550,6 +34588,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
34550
34588
  sessionState.cwd,
34551
34589
  sessionState.additionalDirectories,
34552
34590
  (turnId) => {
34591
+ mcpApprovalTurnId = turnId;
34553
34592
  const turn = { threadId: params.sessionId, turnId };
34554
34593
  activePrompt.currentTurn = turn;
34555
34594
  if (this.promptShouldStop(params.sessionId, activePrompt)) {
@@ -34560,7 +34599,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
34560
34599
  pendingTurnStart?.resolve(turnId);
34561
34600
  onTurnStarted?.();
34562
34601
  },
34563
- () => this.promptShouldStop(params.sessionId, activePrompt)
34602
+ () => this.promptShouldStop(params.sessionId, activePrompt) || sessionState.permissionModeRevision !== permissionModeRevision
34564
34603
  )
34565
34604
  );
34566
34605
  void sendPromptPromise.catch((err) => {
@@ -34640,6 +34679,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
34640
34679
  sessionState.cwd,
34641
34680
  sessionState.additionalDirectories,
34642
34681
  (turnId) => {
34682
+ mcpApprovalTurnId = turnId;
34643
34683
  const turn = { threadId: params.sessionId, turnId };
34644
34684
  activePrompt.currentTurn = turn;
34645
34685
  if (this.promptShouldStop(params.sessionId, activePrompt)) {
@@ -34650,7 +34690,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
34650
34690
  recoverableSessionFailure = sessionState.sessionFailure;
34651
34691
  promptNotificationsActive = true;
34652
34692
  },
34653
- () => this.promptShouldStop(params.sessionId, activePrompt)
34693
+ () => this.promptShouldStop(params.sessionId, activePrompt) || sessionState.permissionModeRevision !== permissionModeRevision
34654
34694
  )
34655
34695
  );
34656
34696
  void implementationPromise.catch((err) => {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.10.0-agentconnect.1",
6
+ "version": "1.10.0-agentconnect.2",
7
7
  "description": "",
8
8
  "main": "dist/index.js",
9
9
  "bin": {