@ccpocket/bridge 1.88.1 → 1.88.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.
@@ -121,6 +121,8 @@ export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
121
121
  private pendingTurnCompletion;
122
122
  private pendingApprovals;
123
123
  private pendingUserInputs;
124
+ private pendingRequestOrder;
125
+ private nextPendingRequestOrder;
124
126
  private lastTokenUsage;
125
127
  /** Full skill metadata from the last `skills/list` response. */
126
128
  private _skills;
@@ -320,9 +322,19 @@ export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
320
322
  toolName: string;
321
323
  input: Record<string, unknown>;
322
324
  } | undefined;
325
+ getPendingPermissions(): Array<{
326
+ toolUseId: string;
327
+ toolName: string;
328
+ input: Record<string, unknown>;
329
+ }>;
323
330
  /** Emit a synthetic tool_result so history replay can match it to a permission_request. */
324
331
  private emitToolResult;
325
332
  private resolvePendingApproval;
333
+ private rememberPendingRequest;
334
+ private forgetPendingRequest;
335
+ private oldestPendingRequestId;
336
+ private pendingRequestIdsInOrder;
337
+ private oldestApprovalCompatibleRequestId;
326
338
  private resolvePendingUserInput;
327
339
  /**
328
340
  * Approve a pending user-input request (McpElicitation fallback).
@@ -77,6 +77,8 @@ export class CodexProcess extends EventEmitter {
77
77
  pendingTurnCompletion = null;
78
78
  pendingApprovals = new Map();
79
79
  pendingUserInputs = new Map();
80
+ pendingRequestOrder = new Map();
81
+ nextPendingRequestOrder = 0;
80
82
  lastTokenUsage = null;
81
83
  /** Full skill metadata from the last `skills/list` response. */
82
84
  _skills = [];
@@ -663,6 +665,7 @@ export class CodexProcess extends EventEmitter {
663
665
  }
664
666
  this.pendingApprovals.clear();
665
667
  this.pendingUserInputs.clear();
668
+ this.pendingRequestOrder.clear();
666
669
  this.stdoutLineChunks = [];
667
670
  this.stdoutLineChars = 0;
668
671
  this.cleanupSteerTempPaths();
@@ -684,6 +687,8 @@ export class CodexProcess extends EventEmitter {
684
687
  this.pendingTurnCompletion = null;
685
688
  this.pendingApprovals.clear();
686
689
  this.pendingUserInputs.clear();
690
+ this.pendingRequestOrder.clear();
691
+ this.nextPendingRequestOrder = 0;
687
692
  this.cleanupSteerTempPaths();
688
693
  this.lastTokenUsage = null;
689
694
  this.startModel = sanitizeCodexModel(options?.model);
@@ -838,21 +843,25 @@ export class CodexProcess extends EventEmitter {
838
843
  }
839
844
  }
840
845
  approve(toolUseId) {
846
+ const targetToolUseId = toolUseId ?? this.oldestApprovalCompatibleRequestId();
847
+ if (!targetToolUseId)
848
+ return false;
841
849
  // Check if this is a plan completion approval
842
850
  if (this.pendingPlanCompletion &&
843
- toolUseId === this.pendingPlanCompletion.toolUseId) {
851
+ targetToolUseId === this.pendingPlanCompletion.toolUseId) {
844
852
  this.handlePlanApproved();
845
853
  return true;
846
854
  }
847
- const pending = this.resolvePendingApproval(toolUseId);
855
+ const pending = this.resolvePendingApproval(targetToolUseId);
848
856
  if (!pending) {
849
857
  // Fallback: McpElicitation lives in pendingUserInputs
850
- if (this.approveUserInput(toolUseId, "Accept"))
858
+ if (this.approveUserInput(targetToolUseId, "Accept"))
851
859
  return true;
852
860
  console.log("[codex-process] approve() called but no pending permission requests");
853
861
  return false;
854
862
  }
855
863
  this.pendingApprovals.delete(pending.toolUseId);
864
+ this.forgetPendingRequest(pending.toolUseId);
856
865
  this.respondToServerRequest(pending.requestId, buildApprovalResponse(pending, "accept"));
857
866
  this.emitToolResult(pending.toolUseId, "Approved", "approved");
858
867
  if (this.pendingApprovals.size === 0 &&
@@ -863,16 +872,25 @@ export class CodexProcess extends EventEmitter {
863
872
  return true;
864
873
  }
865
874
  approveAlways(toolUseId) {
866
- const pending = this.resolvePendingApproval(toolUseId);
875
+ const targetToolUseId = toolUseId ?? this.oldestApprovalCompatibleRequestId();
876
+ if (!targetToolUseId)
877
+ return false;
878
+ if (this.pendingPlanCompletion &&
879
+ targetToolUseId === this.pendingPlanCompletion.toolUseId) {
880
+ this.handlePlanApproved();
881
+ return true;
882
+ }
883
+ const pending = this.resolvePendingApproval(targetToolUseId);
867
884
  if (!pending) {
868
885
  // Fallback: McpElicitation lives in pendingUserInputs
869
- if (this.approveUserInput(toolUseId, "Allow for this session")) {
886
+ if (this.approveUserInput(targetToolUseId, "Allow for this session")) {
870
887
  return true;
871
888
  }
872
889
  console.log("[codex-process] approveAlways() called but no pending permission requests");
873
890
  return false;
874
891
  }
875
892
  this.pendingApprovals.delete(pending.toolUseId);
893
+ this.forgetPendingRequest(pending.toolUseId);
876
894
  this.respondToServerRequest(pending.requestId, buildApprovalResponse(pending, "acceptForSession"));
877
895
  this.emitToolResult(pending.toolUseId, "Approved (always)", "approved_for_session");
878
896
  if (this.pendingApprovals.size === 0 &&
@@ -883,21 +901,25 @@ export class CodexProcess extends EventEmitter {
883
901
  return true;
884
902
  }
885
903
  reject(toolUseId, _message) {
904
+ const targetToolUseId = toolUseId ?? this.oldestApprovalCompatibleRequestId();
905
+ if (!targetToolUseId)
906
+ return false;
886
907
  // Check if this is a plan completion rejection
887
908
  if (this.pendingPlanCompletion &&
888
- toolUseId === this.pendingPlanCompletion.toolUseId) {
909
+ targetToolUseId === this.pendingPlanCompletion.toolUseId) {
889
910
  this.handlePlanRejected(_message);
890
911
  return true;
891
912
  }
892
- const pending = this.resolvePendingApproval(toolUseId);
913
+ const pending = this.resolvePendingApproval(targetToolUseId);
893
914
  if (!pending) {
894
915
  // Fallback: McpElicitation lives in pendingUserInputs
895
- if (this.rejectUserInput(toolUseId, "Decline"))
916
+ if (this.rejectUserInput(targetToolUseId, "Decline"))
896
917
  return true;
897
918
  console.log("[codex-process] reject() called but no pending permission requests");
898
919
  return false;
899
920
  }
900
921
  this.pendingApprovals.delete(pending.toolUseId);
922
+ this.forgetPendingRequest(pending.toolUseId);
901
923
  this.respondToServerRequest(pending.requestId, buildApprovalResponse(pending, resolveApprovalRejectDecision(pending)));
902
924
  this.emitToolResult(pending.toolUseId, "Rejected", "rejected");
903
925
  if (this.pendingApprovals.size === 0 &&
@@ -914,6 +936,7 @@ export class CodexProcess extends EventEmitter {
914
936
  return false;
915
937
  }
916
938
  this.pendingUserInputs.delete(pending.toolUseId);
939
+ this.forgetPendingRequest(pending.toolUseId);
917
940
  this.respondToServerRequest(pending.requestId, buildUserInputResponse(pending, result));
918
941
  this.emitToolResult(pending.toolUseId, "Answered", "answered");
919
942
  if (this.pendingApprovals.size === 0 &&
@@ -1014,9 +1037,11 @@ export class CodexProcess extends EventEmitter {
1014
1037
  }
1015
1038
  }
1016
1039
  getPendingPermission(toolUseId) {
1017
- // Check plan completion first
1040
+ const selectedToolUseId = toolUseId ?? this.oldestPendingRequestId();
1041
+ if (!selectedToolUseId)
1042
+ return undefined;
1018
1043
  if (this.pendingPlanCompletion) {
1019
- if (!toolUseId || toolUseId === this.pendingPlanCompletion.toolUseId) {
1044
+ if (selectedToolUseId === this.pendingPlanCompletion.toolUseId) {
1020
1045
  return {
1021
1046
  toolUseId: this.pendingPlanCompletion.toolUseId,
1022
1047
  toolName: "ExitPlanMode",
@@ -1024,7 +1049,7 @@ export class CodexProcess extends EventEmitter {
1024
1049
  };
1025
1050
  }
1026
1051
  }
1027
- const pending = this.resolvePendingApproval(toolUseId);
1052
+ const pending = this.pendingApprovals.get(selectedToolUseId);
1028
1053
  if (pending) {
1029
1054
  return {
1030
1055
  toolUseId: pending.toolUseId,
@@ -1032,7 +1057,7 @@ export class CodexProcess extends EventEmitter {
1032
1057
  input: { ...pending.input },
1033
1058
  };
1034
1059
  }
1035
- const pendingAsk = this.resolvePendingUserInput(toolUseId);
1060
+ const pendingAsk = this.pendingUserInputs.get(selectedToolUseId);
1036
1061
  if (!pendingAsk)
1037
1062
  return undefined;
1038
1063
  return {
@@ -1041,6 +1066,11 @@ export class CodexProcess extends EventEmitter {
1041
1066
  input: { ...pendingAsk.input },
1042
1067
  };
1043
1068
  }
1069
+ getPendingPermissions() {
1070
+ return this.pendingRequestIdsInOrder()
1071
+ .map((toolUseId) => this.getPendingPermission(toolUseId))
1072
+ .filter((request) => request !== undefined);
1073
+ }
1044
1074
  /** Emit a synthetic tool_result so history replay can match it to a permission_request. */
1045
1075
  emitToolResult(toolUseId, content, permissionOutcome) {
1046
1076
  this.emitMessage({
@@ -1056,6 +1086,39 @@ export class CodexProcess extends EventEmitter {
1056
1086
  const first = this.pendingApprovals.values().next();
1057
1087
  return first.done ? undefined : first.value;
1058
1088
  }
1089
+ rememberPendingRequest(toolUseId) {
1090
+ this.pendingRequestOrder.set(toolUseId, this.nextPendingRequestOrder++);
1091
+ }
1092
+ forgetPendingRequest(toolUseId) {
1093
+ this.pendingRequestOrder.delete(toolUseId);
1094
+ }
1095
+ oldestPendingRequestId() {
1096
+ return this.pendingRequestIdsInOrder()[0];
1097
+ }
1098
+ pendingRequestIdsInOrder() {
1099
+ const pendingIds = new Set([
1100
+ ...this.pendingApprovals.keys(),
1101
+ ...this.pendingUserInputs.keys(),
1102
+ ...(this.pendingPlanCompletion
1103
+ ? [this.pendingPlanCompletion.toolUseId]
1104
+ : []),
1105
+ ]);
1106
+ return [...pendingIds].sort((a, b) => (this.pendingRequestOrder.get(a) ?? Number.MAX_SAFE_INTEGER) -
1107
+ (this.pendingRequestOrder.get(b) ?? Number.MAX_SAFE_INTEGER));
1108
+ }
1109
+ oldestApprovalCompatibleRequestId() {
1110
+ const pendingIds = new Set([
1111
+ ...this.pendingApprovals.keys(),
1112
+ ...[...this.pendingUserInputs.values()]
1113
+ .filter((request) => request.kind !== "questions")
1114
+ .map((request) => request.toolUseId),
1115
+ ...(this.pendingPlanCompletion
1116
+ ? [this.pendingPlanCompletion.toolUseId]
1117
+ : []),
1118
+ ]);
1119
+ return [...pendingIds].sort((a, b) => (this.pendingRequestOrder.get(a) ?? Number.MAX_SAFE_INTEGER) -
1120
+ (this.pendingRequestOrder.get(b) ?? Number.MAX_SAFE_INTEGER))[0];
1121
+ }
1059
1122
  resolvePendingUserInput(toolUseId) {
1060
1123
  if (toolUseId)
1061
1124
  return this.pendingUserInputs.get(toolUseId);
@@ -1070,7 +1133,7 @@ export class CodexProcess extends EventEmitter {
1070
1133
  */
1071
1134
  approveUserInput(toolUseId, result) {
1072
1135
  const pending = this.resolvePendingUserInput(toolUseId);
1073
- if (!pending)
1136
+ if (!pending || pending.kind === "questions")
1074
1137
  return false;
1075
1138
  if (pending.kind === "tool_suggestion") {
1076
1139
  if (pending.input.installState === "needs_auth") {
@@ -1082,6 +1145,7 @@ export class CodexProcess extends EventEmitter {
1082
1145
  return true;
1083
1146
  }
1084
1147
  this.pendingUserInputs.delete(pending.toolUseId);
1148
+ this.forgetPendingRequest(pending.toolUseId);
1085
1149
  this.respondToServerRequest(pending.requestId, buildUserInputResponse(pending, result));
1086
1150
  this.emitToolResult(pending.toolUseId, "Approved", result === "Allow for this session"
1087
1151
  ? "approved_for_session"
@@ -1104,6 +1168,7 @@ export class CodexProcess extends EventEmitter {
1104
1168
  }
1105
1169
  resolveToolSuggestion(pending, toolResult) {
1106
1170
  this.pendingUserInputs.delete(pending.toolUseId);
1171
+ this.forgetPendingRequest(pending.toolUseId);
1107
1172
  this.respondToServerRequest(pending.requestId, {
1108
1173
  action: "accept",
1109
1174
  content: null,
@@ -1125,9 +1190,10 @@ export class CodexProcess extends EventEmitter {
1125
1190
  */
1126
1191
  rejectUserInput(toolUseId, result) {
1127
1192
  const pending = this.resolvePendingUserInput(toolUseId);
1128
- if (!pending)
1193
+ if (!pending || pending.kind === "questions")
1129
1194
  return false;
1130
1195
  this.pendingUserInputs.delete(pending.toolUseId);
1196
+ this.forgetPendingRequest(pending.toolUseId);
1131
1197
  this.respondToServerRequest(pending.requestId, buildUserInputResponse(pending, resolveUserInputRejectResult(pending, result)));
1132
1198
  this.emitToolResult(pending.toolUseId, "Rejected", "rejected");
1133
1199
  if (this.pendingApprovals.size === 0 &&
@@ -1147,6 +1213,8 @@ export class CodexProcess extends EventEmitter {
1147
1213
  const planText = this.pendingPlanCompletion?.planText ?? "";
1148
1214
  const resolvedToolUseId = this.pendingPlanCompletion?.toolUseId;
1149
1215
  this.pendingPlanCompletion = null;
1216
+ if (resolvedToolUseId)
1217
+ this.forgetPendingRequest(resolvedToolUseId);
1150
1218
  this._collaborationMode = "default";
1151
1219
  console.log("[codex-process] Plan approved, switching to Default mode");
1152
1220
  // Emit synthetic tool_result so history replay knows this approval is resolved
@@ -1172,6 +1240,8 @@ export class CodexProcess extends EventEmitter {
1172
1240
  handlePlanRejected(feedback) {
1173
1241
  const resolvedToolUseId = this.pendingPlanCompletion?.toolUseId;
1174
1242
  this.pendingPlanCompletion = null;
1243
+ if (resolvedToolUseId)
1244
+ this.forgetPendingRequest(resolvedToolUseId);
1175
1245
  console.log("[codex-process] Plan rejected, continuing in Plan mode");
1176
1246
  // Stay in Plan mode
1177
1247
  // Emit synthetic tool_result so history replay knows this approval is resolved
@@ -1882,6 +1952,7 @@ export class CodexProcess extends EventEmitter {
1882
1952
  ? { reason: params.reason }
1883
1953
  : {}),
1884
1954
  };
1955
+ this.rememberPendingRequest(toolUseId);
1885
1956
  this.pendingApprovals.set(toolUseId, {
1886
1957
  requestId: id,
1887
1958
  toolUseId,
@@ -1909,6 +1980,7 @@ export class CodexProcess extends EventEmitter {
1909
1980
  ? { reason: params.reason }
1910
1981
  : {}),
1911
1982
  };
1983
+ this.rememberPendingRequest(toolUseId);
1912
1984
  this.pendingApprovals.set(toolUseId, {
1913
1985
  requestId: id,
1914
1986
  toolUseId,
@@ -1940,6 +2012,7 @@ export class CodexProcess extends EventEmitter {
1940
2012
  isSecret: q.isSecret,
1941
2013
  })),
1942
2014
  };
2015
+ this.rememberPendingRequest(toolUseId);
1943
2016
  this.pendingUserInputs.set(toolUseId, {
1944
2017
  requestId: id,
1945
2018
  toolUseId,
@@ -1971,6 +2044,7 @@ export class CodexProcess extends EventEmitter {
1971
2044
  ? { reason: params.reason }
1972
2045
  : {}),
1973
2046
  };
2047
+ this.rememberPendingRequest(toolUseId);
1974
2048
  this.pendingApprovals.set(toolUseId, {
1975
2049
  requestId: id,
1976
2050
  toolUseId,
@@ -1994,6 +2068,7 @@ export class CodexProcess extends EventEmitter {
1994
2068
  const toolName = elicitation.kind === "tool_suggestion"
1995
2069
  ? "ToolSuggestion"
1996
2070
  : "McpElicitation";
2071
+ this.rememberPendingRequest(toolUseId);
1997
2072
  this.pendingUserInputs.set(toolUseId, {
1998
2073
  requestId: id,
1999
2074
  toolUseId,
@@ -2306,6 +2381,7 @@ export class CodexProcess extends EventEmitter {
2306
2381
  // Plan mode: emit synthetic plan approval and wait for user decision
2307
2382
  if (this._collaborationMode === "plan" && this.lastPlanItemText) {
2308
2383
  const toolUseId = `plan_${randomUUID()}`;
2384
+ this.rememberPendingRequest(toolUseId);
2309
2385
  this.pendingPlanCompletion = {
2310
2386
  toolUseId,
2311
2387
  planText: this.lastPlanItemText,
@@ -2932,6 +3008,7 @@ export class CodexProcess extends EventEmitter {
2932
3008
  const approval = [...this.pendingApprovals.values()].find((entry) => entry.requestId === requestId);
2933
3009
  if (approval) {
2934
3010
  this.pendingApprovals.delete(approval.toolUseId);
3011
+ this.forgetPendingRequest(approval.toolUseId);
2935
3012
  this.emitMessage({
2936
3013
  type: "permission_resolved",
2937
3014
  toolUseId: approval.toolUseId,
@@ -2940,6 +3017,7 @@ export class CodexProcess extends EventEmitter {
2940
3017
  const inputRequest = [...this.pendingUserInputs.values()].find((entry) => entry.requestId === requestId);
2941
3018
  if (inputRequest) {
2942
3019
  this.pendingUserInputs.delete(inputRequest.toolUseId);
3020
+ this.forgetPendingRequest(inputRequest.toolUseId);
2943
3021
  this.emitMessage({
2944
3022
  type: "permission_resolved",
2945
3023
  toolUseId: inputRequest.toolUseId,