@agentclientprotocol/codex-acp 1.1.7 → 1.1.8

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 +306 -15
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -23585,7 +23585,9 @@ function sameThreadGoalSnapshot(left, right) {
23585
23585
  var CodexEventHandler = class {
23586
23586
  connection;
23587
23587
  sessionState;
23588
+ supportsPlanUpdates;
23588
23589
  failure = null;
23590
+ completedPlan = null;
23589
23591
  activeFuzzyFileSearchSessions = /* @__PURE__ */ new Set();
23590
23592
  activeGuardianApprovalReviews = /* @__PURE__ */ new Set();
23591
23593
  activeImageGenerationItems = /* @__PURE__ */ new Set();
@@ -23596,13 +23598,19 @@ var CodexEventHandler = class {
23596
23598
  terminalCommandOutputIds = /* @__PURE__ */ new Set();
23597
23599
  agentMessagePhases = /* @__PURE__ */ new Map();
23598
23600
  activeSubAgentActivities = /* @__PURE__ */ new Set();
23599
- constructor(connection, sessionState) {
23601
+ constructor(connection, sessionState, supportsPlanUpdates = false) {
23600
23602
  this.connection = connection;
23601
23603
  this.sessionState = sessionState;
23604
+ this.supportsPlanUpdates = supportsPlanUpdates;
23602
23605
  }
23603
23606
  getFailure() {
23604
23607
  return this.failure;
23605
23608
  }
23609
+ takeCompletedPlan() {
23610
+ const plan = this.completedPlan;
23611
+ this.completedPlan = null;
23612
+ return plan;
23613
+ }
23606
23614
  async handleNotification(notification) {
23607
23615
  const session = new ACPSessionConnection(this.connection, this.sessionState.sessionId);
23608
23616
  const updateEvent = await this.createUpdateEvent(notification);
@@ -23796,8 +23804,9 @@ ${event.details}` : "";
23796
23804
  return null;
23797
23805
  }
23798
23806
  const text = this.planDeltaTextByItemId.get(event.itemId) ?? "";
23799
- this.planDeltaTextByItemId.set(event.itemId, text + event.delta);
23800
- return null;
23807
+ const updatedText = text + event.delta;
23808
+ this.planDeltaTextByItemId.set(event.itemId, updatedText);
23809
+ return this.supportsPlanUpdates ? this.createPlanUpdateEvent(updatedText, event.itemId) : null;
23801
23810
  }
23802
23811
  createReasoningSectionBreakEvent(event) {
23803
23812
  this.seenReasoningDeltaItemIds.add(event.itemId);
@@ -23929,7 +23938,18 @@ ${event.details}` : "";
23929
23938
  if (text.length === 0) {
23930
23939
  return null;
23931
23940
  }
23932
- return this.createPlanTextEvent(text, item.id);
23941
+ this.completedPlan = { itemId: item.id, text };
23942
+ return this.supportsPlanUpdates ? this.createPlanUpdateEvent(text, item.id) : this.createPlanTextEvent(text, item.id);
23943
+ }
23944
+ createPlanUpdateEvent(text, planId) {
23945
+ return {
23946
+ sessionUpdate: "plan_update",
23947
+ plan: {
23948
+ type: "markdown",
23949
+ planId,
23950
+ content: text
23951
+ }
23952
+ };
23933
23953
  }
23934
23954
  createPlanTextEvent(text, messageId) {
23935
23955
  return createAgentTextMessageChunk(
@@ -24182,12 +24202,17 @@ var ApprovalOptionId = {
24182
24202
  };
24183
24203
 
24184
24204
  // src/CodexApprovalHandler.ts
24185
- function permissionOption(optionId, name, kind, codexMeta) {
24205
+ function permissionOption(optionId, name, kind, codexMeta, permission) {
24186
24206
  return {
24187
24207
  optionId,
24188
24208
  name,
24189
24209
  kind,
24190
- ...codexMeta ? { _meta: { codex: codexMeta } } : {}
24210
+ ...codexMeta || permission ? {
24211
+ _meta: {
24212
+ ...permission ? { permission } : {},
24213
+ ...codexMeta ? { codex: codexMeta } : {}
24214
+ }
24215
+ } : {}
24191
24216
  };
24192
24217
  }
24193
24218
  var CodexApprovalHandler = class {
@@ -24294,13 +24319,15 @@ var CodexApprovalHandler = class {
24294
24319
  ApprovalOptionId.AllowPermissionsForSession,
24295
24320
  "Allow for Session",
24296
24321
  "allow_always",
24297
- { decision: "allowPermissionsForSession", permissions: params.permissions }
24322
+ { decision: "allowPermissionsForSession", permissions: params.permissions },
24323
+ this.permissionGrantMetadata(params.permissions, "session")
24298
24324
  ),
24299
24325
  permissionOption(
24300
24326
  ApprovalOptionId.AllowPermissionsForTurn,
24301
24327
  "Allow Once",
24302
24328
  "allow_once",
24303
- { decision: "allowPermissionsForTurn", permissions: params.permissions }
24329
+ { decision: "allowPermissionsForTurn", permissions: params.permissions },
24330
+ this.permissionGrantMetadata(params.permissions, "turn")
24304
24331
  ),
24305
24332
  permissionOption(
24306
24333
  ApprovalOptionId.RejectPermissions,
@@ -24362,7 +24389,24 @@ var CodexApprovalHandler = class {
24362
24389
  ApprovalOptionId.AllowAlways,
24363
24390
  params.networkApprovalContext ? "Allow Host for Session" : "Allow for Session",
24364
24391
  "allow_always",
24365
- { decision: "acceptForSession" }
24392
+ { decision: "acceptForSession" },
24393
+ params.networkApprovalContext ? {
24394
+ version: 1,
24395
+ changes: [{
24396
+ type: "grant",
24397
+ operation: "grant",
24398
+ description: `Allow access to ${params.networkApprovalContext.host} for this session`,
24399
+ lifetime: { scope: "session" },
24400
+ targets: [{
24401
+ type: "network",
24402
+ matcher: {
24403
+ type: "host",
24404
+ host: params.networkApprovalContext.host,
24405
+ protocol: params.networkApprovalContext.protocol
24406
+ }
24407
+ }]
24408
+ }]
24409
+ } : void 0
24366
24410
  ),
24367
24411
  decision: "acceptForSession"
24368
24412
  }
@@ -24376,6 +24420,22 @@ var CodexApprovalHandler = class {
24376
24420
  {
24377
24421
  decision: "acceptWithExecpolicyAmendment",
24378
24422
  execpolicyAmendment: params.proposedExecpolicyAmendment
24423
+ },
24424
+ {
24425
+ version: 1,
24426
+ changes: [{
24427
+ type: "policy_rule",
24428
+ operation: "add",
24429
+ ruleBehavior: "allow",
24430
+ description: `Allow commands starting with ${params.proposedExecpolicyAmendment.join(" ")}`,
24431
+ targets: [{
24432
+ type: "command",
24433
+ matcher: {
24434
+ type: "argv_prefix",
24435
+ argv: params.proposedExecpolicyAmendment
24436
+ }
24437
+ }]
24438
+ }]
24379
24439
  }
24380
24440
  ),
24381
24441
  decision: {
@@ -24394,6 +24454,22 @@ var CodexApprovalHandler = class {
24394
24454
  {
24395
24455
  decision: "applyNetworkPolicyAmendment",
24396
24456
  networkPolicyAmendment: amendment
24457
+ },
24458
+ {
24459
+ version: 1,
24460
+ changes: [{
24461
+ type: "policy_rule",
24462
+ operation: "add",
24463
+ ruleBehavior: amendment.action,
24464
+ description: amendment.action === "allow" ? `Allow access to ${amendment.host}` : `Block access to ${amendment.host}`,
24465
+ targets: [{
24466
+ type: "network",
24467
+ matcher: {
24468
+ type: "host",
24469
+ host: amendment.host
24470
+ }
24471
+ }]
24472
+ }]
24397
24473
  }
24398
24474
  ),
24399
24475
  decision: {
@@ -24420,7 +24496,21 @@ var CodexApprovalHandler = class {
24420
24496
  ApprovalOptionId.AllowAlways,
24421
24497
  params.grantRoot ? "Allow Root for Session" : "Allow for Session",
24422
24498
  "allow_always",
24423
- { decision: "acceptForSession", grantRoot: params.grantRoot ?? null }
24499
+ { decision: "acceptForSession", grantRoot: params.grantRoot ?? null },
24500
+ params.grantRoot ? {
24501
+ version: 1,
24502
+ changes: [{
24503
+ type: "grant",
24504
+ operation: "grant",
24505
+ description: `Allow writes under ${params.grantRoot} for this session`,
24506
+ lifetime: { scope: "session" },
24507
+ targets: [{
24508
+ type: "filesystem",
24509
+ access: ["write"],
24510
+ matcher: { type: "directory", path: params.grantRoot }
24511
+ }]
24512
+ }]
24513
+ } : void 0
24424
24514
  ),
24425
24515
  decision: "acceptForSession"
24426
24516
  },
@@ -24443,6 +24533,68 @@ var CodexApprovalHandler = class {
24443
24533
  ...permissions.fileSystem ? { fileSystem: permissions.fileSystem } : {}
24444
24534
  };
24445
24535
  }
24536
+ permissionGrantMetadata(permissions, scope) {
24537
+ const changes = [];
24538
+ const lifetime = { scope };
24539
+ const suffix = scope === "session" ? " for this session" : " for this turn";
24540
+ if (permissions.network?.enabled !== null && permissions.network?.enabled !== void 0) {
24541
+ const allowed = permissions.network.enabled;
24542
+ changes.push({
24543
+ type: allowed ? "grant" : "policy_rule",
24544
+ operation: allowed ? "grant" : "add",
24545
+ ...allowed ? {} : { ruleBehavior: "deny" },
24546
+ description: `${allowed ? "Allow" : "Deny"} network access${suffix}`,
24547
+ lifetime,
24548
+ targets: [{ type: "network", matcher: { type: "any" } }]
24549
+ });
24550
+ }
24551
+ const fileSystem = permissions.fileSystem;
24552
+ for (const path6 of fileSystem?.read ?? []) {
24553
+ changes.push(this.fileSystemGrantChange(path6, "read", lifetime, suffix));
24554
+ }
24555
+ for (const path6 of fileSystem?.write ?? []) {
24556
+ changes.push(this.fileSystemGrantChange(path6, "write", lifetime, suffix));
24557
+ }
24558
+ for (const entry of fileSystem?.entries ?? []) {
24559
+ const matcher = (() => {
24560
+ switch (entry.path.type) {
24561
+ case "path":
24562
+ return { type: "exact_path", path: entry.path.path };
24563
+ case "glob_pattern":
24564
+ return { type: "glob", pattern: entry.path.pattern };
24565
+ case "special":
24566
+ return { type: "special", provider: "codex", value: entry.path.value };
24567
+ }
24568
+ })();
24569
+ const pathDescription = entry.path.type === "path" ? entry.path.path : entry.path.type === "glob_pattern" ? entry.path.pattern : JSON.stringify(entry.path.value);
24570
+ changes.push({
24571
+ type: entry.access === "deny" ? "policy_rule" : "grant",
24572
+ operation: entry.access === "deny" ? "add" : "grant",
24573
+ ...entry.access === "deny" ? { ruleBehavior: "deny" } : {},
24574
+ description: entry.access === "deny" ? `Deny filesystem access to ${pathDescription}${suffix}` : `Allow ${entry.access} access to ${pathDescription}${suffix}`,
24575
+ lifetime,
24576
+ targets: [{
24577
+ type: "filesystem",
24578
+ ...entry.access === "deny" ? {} : { access: [entry.access] },
24579
+ matcher
24580
+ }]
24581
+ });
24582
+ }
24583
+ return changes.length > 0 ? { version: 1, changes } : void 0;
24584
+ }
24585
+ fileSystemGrantChange(path6, access, lifetime, suffix) {
24586
+ return {
24587
+ type: "grant",
24588
+ operation: "grant",
24589
+ description: `Allow ${access} access to ${path6}${suffix}`,
24590
+ lifetime,
24591
+ targets: [{
24592
+ type: "filesystem",
24593
+ access: [access],
24594
+ matcher: { type: "exact_path", path: path6 }
24595
+ }]
24596
+ };
24597
+ }
24446
24598
  networkPolicyAmendmentOptionId(index) {
24447
24599
  return `${ApprovalOptionId.ApplyNetworkPolicyAmendment}:${index}`;
24448
24600
  }
@@ -25913,7 +26065,7 @@ var package_default = {
25913
26065
  publishConfig: {
25914
26066
  access: "public"
25915
26067
  },
25916
- version: "1.1.7",
26068
+ version: "1.1.8",
25917
26069
  description: "",
25918
26070
  main: "dist/index.js",
25919
26071
  bin: {
@@ -28413,7 +28565,14 @@ function isJetBrains2026_1Client(clientInfo) {
28413
28565
  return (isIntelliJPlatform || isJetBrainsClient) && clientInfo.version.startsWith("2026.1");
28414
28566
  }
28415
28567
 
28568
+ // src/PlanCapabilities.ts
28569
+ function clientSupportsPlanUpdates(clientCapabilities) {
28570
+ return clientCapabilities?.plan != null;
28571
+ }
28572
+
28416
28573
  // src/CodexAcpServer.ts
28574
+ var IMPLEMENT_PLAN_OPTION_ID = "implement_plan";
28575
+ var REVISE_PLAN_OPTION_ID = "revise_plan";
28417
28576
  var CodexAcpServer = class _CodexAcpServer {
28418
28577
  static MODEL_NAME_TOKEN_OVERRIDES = {
28419
28578
  gpt: "GPT",
@@ -29532,7 +29691,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
29532
29691
  case "contextCompaction":
29533
29692
  return [createCompletedContextCompactionUpdate(item)];
29534
29693
  case "plan":
29535
- return [this.createPlanMessageUpdate(item)];
29694
+ return item.text.length > 0 ? [this.createPlanHistoryUpdate(item)] : [];
29536
29695
  }
29537
29696
  }
29538
29697
  createUserMessageUpdates(item) {
@@ -29573,7 +29732,17 @@ Check ${configPath} and project .codex directories, especially their config.toml
29573
29732
  }
29574
29733
  };
29575
29734
  }
29576
- createPlanMessageUpdate(item) {
29735
+ createPlanHistoryUpdate(item) {
29736
+ if (clientSupportsPlanUpdates(this.clientCapabilities)) {
29737
+ return {
29738
+ sessionUpdate: "plan_update",
29739
+ plan: {
29740
+ type: "markdown",
29741
+ planId: item.id,
29742
+ content: item.text
29743
+ }
29744
+ };
29745
+ }
29577
29746
  return createAgentTextMessageChunk(
29578
29747
  item.text,
29579
29748
  item.id,
@@ -29867,7 +30036,11 @@ Check ${configPath} and project .codex directories, especially their config.toml
29867
30036
  };
29868
30037
  const disposePromptRequestCancellation = this.observePromptRequestCancellation(signal, sessionState, activePrompt);
29869
30038
  try {
29870
- const eventHandler = new CodexEventHandler(this.connection, sessionState);
30039
+ const eventHandler = new CodexEventHandler(
30040
+ this.connection,
30041
+ sessionState,
30042
+ clientSupportsPlanUpdates(this.clientCapabilities)
30043
+ );
29871
30044
  const approvalHandler = new CodexApprovalHandler(this.connection, sessionState, activePrompt.signal);
29872
30045
  const elicitationHandler = new CodexElicitationHandler(
29873
30046
  this.connection,
@@ -29994,7 +30167,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
29994
30167
  logger.error(`Prompt for cancelled session ${params.sessionId} failed after prompt returned`, err);
29995
30168
  }
29996
30169
  });
29997
- const turnCompleted = await Promise.race([
30170
+ let turnCompleted = await Promise.race([
29998
30171
  sendPromptPromise,
29999
30172
  activePrompt.closeSignal,
30000
30173
  this.cancelBeforeTurnStarted(activePrompt)
@@ -30011,6 +30184,73 @@ Check ${configPath} and project .codex directories, especially their config.toml
30011
30184
  if (error51) {
30012
30185
  throw error51;
30013
30186
  }
30187
+ const completedPlan = eventHandler.takeCompletedPlan();
30188
+ if (completedPlan !== null && sessionState.collaborationMode === PLAN_COLLABORATION_MODE && !this.promptShouldStop(params.sessionId, activePrompt)) {
30189
+ const approved = await this.requestPlanImplementationPermission(
30190
+ sessionState,
30191
+ completedPlan,
30192
+ activePrompt.signal
30193
+ );
30194
+ if (this.promptShouldStop(params.sessionId, activePrompt)) {
30195
+ return this.cancelledPromptResponse(sessionState);
30196
+ }
30197
+ if (approved && !this.promptShouldStop(params.sessionId, activePrompt)) {
30198
+ await this.applyCollaborationModeChange(sessionState, DEFAULT_COLLABORATION_MODE);
30199
+ const session = new ACPSessionConnection(this.connection, sessionState.sessionId);
30200
+ await session.update({
30201
+ sessionUpdate: "config_option_update",
30202
+ configOptions: this.createSessionConfigOptions(sessionState)
30203
+ });
30204
+ const implementationRequest = {
30205
+ sessionId: params.sessionId,
30206
+ prompt: [{ type: "text", text: "Implement the approved plan." }]
30207
+ };
30208
+ activePrompt.currentTurn = null;
30209
+ const implementationPromise = this.runWithProcessCheck(
30210
+ () => this.codexAcpClient.sendPrompt(
30211
+ implementationRequest,
30212
+ agentMode,
30213
+ modelId,
30214
+ serviceTier,
30215
+ disableSummary,
30216
+ sessionState.cwd,
30217
+ sessionState.additionalDirectories,
30218
+ (turnId) => {
30219
+ const turn = { threadId: params.sessionId, turnId };
30220
+ activePrompt.currentTurn = turn;
30221
+ if (this.promptShouldStop(params.sessionId, activePrompt)) {
30222
+ this.interruptLateStartedTurn(turn);
30223
+ return;
30224
+ }
30225
+ sessionState.currentTurnId = turnId;
30226
+ },
30227
+ () => this.promptShouldStop(params.sessionId, activePrompt)
30228
+ )
30229
+ );
30230
+ void implementationPromise.catch((err) => {
30231
+ if (this.activePrompts.get(params.sessionId) !== activePrompt) {
30232
+ logger.error(`Implementation turn for cancelled prompt ${params.sessionId} failed after prompt returned`, err);
30233
+ }
30234
+ });
30235
+ turnCompleted = await Promise.race([
30236
+ implementationPromise,
30237
+ activePrompt.closeSignal,
30238
+ this.cancelBeforeTurnStarted(activePrompt)
30239
+ ]);
30240
+ if (turnCompleted === null) {
30241
+ return this.cancelledPromptResponse(sessionState);
30242
+ }
30243
+ await this.codexAcpClient.waitForSessionNotifications(params.sessionId);
30244
+ if (turnCompleted.turn.status === "interrupted") {
30245
+ await this.notifyConversationInterrupted(params.sessionId);
30246
+ return this.cancelledPromptResponse(sessionState);
30247
+ }
30248
+ const implementationError = eventHandler.getFailure();
30249
+ if (implementationError) {
30250
+ throw implementationError;
30251
+ }
30252
+ }
30253
+ }
30014
30254
  await this.publishFallbackSessionTitle(
30015
30255
  sessionState,
30016
30256
  this.createPromptFallbackTitle(params.prompt)
@@ -30035,6 +30275,57 @@ Check ${configPath} and project .codex directories, especially their config.toml
30035
30275
  activePrompt.complete();
30036
30276
  }
30037
30277
  }
30278
+ async requestPlanImplementationPermission(sessionState, plan, cancellationSignal) {
30279
+ const toolCallId = `plan-review:${plan.itemId}`;
30280
+ try {
30281
+ const response = await this.connection.request(
30282
+ methods.client.session.requestPermission,
30283
+ {
30284
+ sessionId: sessionState.sessionId,
30285
+ toolCall: {
30286
+ toolCallId,
30287
+ title: "Implement this plan?",
30288
+ kind: "switch_mode",
30289
+ status: "pending",
30290
+ rawInput: { plan: plan.text }
30291
+ },
30292
+ options: [
30293
+ {
30294
+ optionId: IMPLEMENT_PLAN_OPTION_ID,
30295
+ name: "Yes, implement this plan",
30296
+ kind: "allow_once"
30297
+ },
30298
+ {
30299
+ optionId: REVISE_PLAN_OPTION_ID,
30300
+ name: "No, and tell Codex what to do differently",
30301
+ kind: "reject_once"
30302
+ }
30303
+ ],
30304
+ _meta: {
30305
+ codex: {
30306
+ kind: "plan_review",
30307
+ planItemId: plan.itemId
30308
+ }
30309
+ }
30310
+ },
30311
+ { cancellationSignal }
30312
+ );
30313
+ const approved = response.outcome.outcome === "selected" && response.outcome.optionId === IMPLEMENT_PLAN_OPTION_ID;
30314
+ await this.connection.notify(methods.client.session.update, {
30315
+ sessionId: sessionState.sessionId,
30316
+ update: {
30317
+ sessionUpdate: "tool_call_update",
30318
+ toolCallId,
30319
+ status: "completed",
30320
+ rawOutput: approved ? "User approved the plan." : "User kept the session in plan mode."
30321
+ }
30322
+ });
30323
+ return approved;
30324
+ } catch (error51) {
30325
+ logger.error("Error requesting plan implementation permission", error51);
30326
+ return false;
30327
+ }
30328
+ }
30038
30329
  cancelledPromptResponse(sessionState) {
30039
30330
  return {
30040
30331
  stopReason: "cancelled",
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.1.7",
6
+ "version": "1.1.8",
7
7
  "description": "",
8
8
  "main": "dist/index.js",
9
9
  "bin": {