@openclaw/codex 2026.5.3 → 2026.5.4-beta.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.
@@ -1,12 +1,13 @@
1
- import { i as resolveCodexAppServerRuntimeOptions, r as readCodexPluginConfig } from "./config-CkkoMeqF.js";
2
- import { i as readCodexDynamicToolCallParams, r as assertCodexTurnStartResponse, s as readCodexTurn } from "./protocol-validators-Cpopom3_.js";
1
+ import { i as resolveCodexAppServerRuntimeOptions, r as readCodexPluginConfig } from "./config-ByrA30No.js";
2
+ import { i as readCodexDynamicToolCallParams, r as assertCodexTurnStartResponse, s as readCodexTurn } from "./protocol-validators-Dky2yV4W.js";
3
3
  import { t as isJsonObject } from "./protocol-C9UWI98H.js";
4
- import { i as isCodexAppServerConnectionClosedError, r as isCodexAppServerApprovalRequest } from "./client-chGfNrq5.js";
5
- import { a as refreshCodexAppServerAuthTokens, o as resolveCodexAppServerAuthProfileId, s as resolveCodexAppServerAuthProfileIdForAgent, t as clearSharedCodexAppServerClientIfCurrent } from "./shared-client-0El8eRIE.js";
4
+ import { i as isCodexAppServerConnectionClosedError, r as isCodexAppServerApprovalRequest } from "./client-BGbqC7jk.js";
5
+ import { r as formatCodexDisplayText, u as formatCodexUsageLimitErrorMessage } from "./command-formatters-PiJcdUbu.js";
6
+ import { a as refreshCodexAppServerAuthTokens, o as resolveCodexAppServerAuthProfileId, s as resolveCodexAppServerAuthProfileIdForAgent, t as clearSharedCodexAppServerClientIfCurrent } from "./shared-client-Dfk3Enm-.js";
6
7
  import { i as readCodexAppServerBinding } from "./session-binding-DuJYTJQy.js";
7
- import { a as startOrResumeThread, i as buildTurnStartParams, o as createCodexDynamicToolBridge, s as applyCodexDynamicToolProfile, t as buildDeveloperInstructions } from "./thread-lifecycle-OAlp5Pku.js";
8
- import { n as defaultCodexAppServerClientFactory, t as createCodexAppServerClientFactoryTestHooks } from "./client-factory-MaOMEltT.js";
9
- import { t as ensureCodexComputerUse } from "./computer-use-amNk2zrB.js";
8
+ import { a as buildTurnStartParams, c as createCodexDynamicToolBridge, l as applyCodexDynamicToolProfile, n as buildDeveloperInstructions, o as codexDynamicToolsFingerprint, s as startOrResumeThread, t as areCodexDynamicToolFingerprintsCompatible } from "./thread-lifecycle-CzllX4PU.js";
9
+ import { n as defaultCodexAppServerClientFactory, t as createCodexAppServerClientFactoryTestHooks } from "./client-factory--cll1Fba.js";
10
+ import { n as rememberCodexRateLimits, r as ensureCodexComputerUse, t as readRecentCodexRateLimits } from "./rate-limit-cache-t6ebYmfS.js";
10
11
  import { createHash } from "node:crypto";
11
12
  import { TOOL_PROGRESS_OUTPUT_MAX_CHARS, acquireSessionWriteLock, appendSessionTranscriptMessage, assembleHarnessContextEngine, bootstrapHarnessContextEngine, buildEmbeddedAttemptToolRunContext, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, callGatewayTool, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, embeddedAgentLog, emitAgentEvent, emitSessionTranscriptUpdate, finalizeHarnessContextEngineTurn, formatApprovalDisplayPath, formatErrorMessage, formatToolAggregate, formatToolProgressOutput, inferToolMetaFromArgs, isActiveHarnessContextEngine, isSubagentSessionKey, normalizeAgentRuntimeTools, normalizeUsage, registerNativeHookRelay, resolveAgentHarnessBeforePromptBuildResult, resolveAttemptSpawnWorkspaceDir, resolveBootstrapContextForRun, resolveModelAuthMode, resolveOpenClawAgentDir, resolveSandboxContext, resolveSessionAgentIds, resolveSessionWriteLockAcquireTimeoutMs, resolveUserPath, runAgentCleanupStep, runAgentHarnessAfterCompactionHook, runAgentHarnessAgentEndHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessBeforeMessageWriteHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
12
13
  import fs from "node:fs/promises";
@@ -80,6 +81,7 @@ function truncateForGateway(value, maxLength) {
80
81
  const PERMISSION_DESCRIPTION_MAX_LENGTH = 700;
81
82
  const PERMISSION_SAMPLE_LIMIT = 2;
82
83
  const PERMISSION_VALUE_MAX_LENGTH = 48;
84
+ const COMMAND_PREVIEW_WITH_DETAILS_MAX_LENGTH = 80;
83
85
  const APPROVAL_PREVIEW_SCAN_MAX_LENGTH = 4096;
84
86
  const APPROVAL_PREVIEW_OMITTED = "[preview truncated or unsafe content omitted]";
85
87
  const ANSI_OSC_SEQUENCE_RE$1 = new RegExp(String.raw`(?:\u001b]|\u009d)[^\u001b\u009c\u0007]*(?:\u0007|\u001b\\|\u009c)`, "g");
@@ -153,7 +155,7 @@ async function handleCodexAppServerApprovalRequest(params) {
153
155
  title: context.title,
154
156
  ...context.eventDetails,
155
157
  ...approvalEventScope(params.method, cancelled ? "cancelled" : "denied"),
156
- message: cancelled ? "Codex app-server approval cancelled because the run stopped." : `Codex app-server approval route failed: ${formatErrorMessage$1(error)}`
158
+ message: cancelled ? "Codex app-server approval cancelled because the run stopped." : `Codex app-server approval route failed: ${formatCodexDisplayText(formatErrorMessage$1(error))}`
157
159
  });
158
160
  return buildApprovalResponse(params.method, context.requestParams, cancelled ? "cancelled" : "denied");
159
161
  }
@@ -181,7 +183,8 @@ function matchesCurrentTurn$1(requestParams, threadId, turnId) {
181
183
  }
182
184
  function buildApprovalContext(params) {
183
185
  const itemId = readString$4(params.requestParams, "itemId") ?? readString$4(params.requestParams, "callId") ?? readString$4(params.requestParams, "approvalId");
184
- const commandPreview = sanitizeApprovalPreview(readDisplayCommandPreview(params.requestParams), 180);
186
+ const commandDetailLines = params.method === "item/commandExecution/requestApproval" ? describeCommandApprovalDetails(params.requestParams) : [];
187
+ const commandPreview = sanitizeApprovalPreview(readDisplayCommandPreview(params.requestParams), commandDetailLines.length > 0 ? COMMAND_PREVIEW_WITH_DETAILS_MAX_LENGTH : 180);
185
188
  const reasonPreview = sanitizeApprovalPreview(readStringPreview(params.requestParams, "reason"), 180);
186
189
  const command = commandPreview.text;
187
190
  const reason = reasonPreview.text;
@@ -192,7 +195,11 @@ function buildApprovalContext(params) {
192
195
  return {
193
196
  kind,
194
197
  title,
195
- description: permissionLines.length > 0 ? joinDescriptionLinesWithinLimit(permissionLines, PERMISSION_DESCRIPTION_MAX_LENGTH) : [subject, params.paramsForRun.sessionKey && `Session: ${params.paramsForRun.sessionKey}`].filter(Boolean).join("\n"),
198
+ description: permissionLines.length > 0 ? joinDescriptionLinesWithinLimit(permissionLines, PERMISSION_DESCRIPTION_MAX_LENGTH) : [
199
+ subject,
200
+ ...commandDetailLines,
201
+ params.paramsForRun.sessionKey && `Session: ${params.paramsForRun.sessionKey}`
202
+ ].filter(Boolean).join("\n"),
196
203
  severity: kind === "exec" ? "warning" : "info",
197
204
  toolName: kind === "exec" ? "codex_command_approval" : params.method === "item/permissions/requestApproval" ? "codex_permission_approval" : "codex_file_approval",
198
205
  itemId,
@@ -235,46 +242,101 @@ function unsupportedApprovalResponse() {
235
242
  };
236
243
  }
237
244
  function describeRequestedPermissions(requestParams) {
238
- const permissions = requestedPermissions(requestParams);
245
+ return describePermissionProfile(requestedPermissions(requestParams), "Permissions");
246
+ }
247
+ function describeCommandApprovalDetails(requestParams) {
248
+ const lines = [];
249
+ const additionalPermissions = isJsonObject(requestParams?.additionalPermissions) ? requestParams.additionalPermissions : void 0;
250
+ if (additionalPermissions) lines.push(...describePermissionProfile(additionalPermissions, "Additional permissions"));
251
+ const execpolicySummary = summarizeStringArray(requestParams?.proposedExecpolicyAmendment, "Proposed exec policy", sanitizePermissionScalar);
252
+ if (execpolicySummary) lines.push(execpolicySummary);
253
+ const networkAmendmentSummary = summarizeNetworkPolicyAmendments(requestParams?.proposedNetworkPolicyAmendments);
254
+ if (networkAmendmentSummary) lines.push(networkAmendmentSummary);
255
+ return lines;
256
+ }
257
+ function describePermissionProfile(permissions, label) {
239
258
  const lines = [];
240
259
  const kinds = [];
241
260
  const risks = /* @__PURE__ */ new Set();
242
261
  if (isJsonObject(permissions.network)) kinds.push("network");
243
262
  if (isJsonObject(permissions.fileSystem)) kinds.push("fileSystem");
244
- if (kinds.length > 0) lines.push(`Permissions: ${kinds.join(", ")}`);
263
+ if (kinds.length > 0) lines.push(`${label}: ${kinds.join(", ")}`);
245
264
  let networkSummary;
246
- if (isJsonObject(permissions.network)) networkSummary = summarizePermissionRecord(permissions.network, risks, [{
247
- key: "allowHosts",
248
- label: "allowHosts",
249
- sanitize: sanitizePermissionHostValue,
250
- risksFor: permissionHostRisks
251
- }]);
265
+ if (isJsonObject(permissions.network)) {
266
+ const summaries = [summarizeNetworkEnabledPermission(permissions.network, risks), summarizePermissionRecord(permissions.network, risks, [{
267
+ key: "allowHosts",
268
+ label: "allowHosts",
269
+ sanitize: sanitizePermissionHostValue,
270
+ risksFor: permissionHostRisks
271
+ }])].filter((summary) => Boolean(summary));
272
+ networkSummary = summaries.length > 0 ? summaries.join("; ") : void 0;
273
+ }
252
274
  let fileSystemSummary;
253
- if (isJsonObject(permissions.fileSystem)) fileSystemSummary = summarizePermissionRecord(permissions.fileSystem, risks, [
254
- {
255
- key: "roots",
256
- label: "roots",
257
- sanitize: sanitizePermissionPathValue,
258
- risksFor: permissionPathRisks
259
- },
260
- {
261
- key: "readPaths",
262
- label: "readPaths",
263
- sanitize: sanitizePermissionPathValue,
264
- risksFor: permissionPathRisks
265
- },
266
- {
267
- key: "writePaths",
268
- label: "writePaths",
269
- sanitize: sanitizePermissionPathValue,
270
- risksFor: permissionPathRisks
271
- }
272
- ]);
275
+ if (isJsonObject(permissions.fileSystem)) {
276
+ const summaries = [summarizePermissionRecord(permissions.fileSystem, risks, [
277
+ {
278
+ key: "read",
279
+ label: "read",
280
+ sanitize: sanitizePermissionPathValue,
281
+ risksFor: permissionPathRisks
282
+ },
283
+ {
284
+ key: "write",
285
+ label: "write",
286
+ sanitize: sanitizePermissionPathValue,
287
+ risksFor: permissionPathRisks
288
+ },
289
+ {
290
+ key: "roots",
291
+ label: "roots",
292
+ sanitize: sanitizePermissionPathValue,
293
+ risksFor: permissionPathRisks
294
+ },
295
+ {
296
+ key: "readPaths",
297
+ label: "readPaths",
298
+ sanitize: sanitizePermissionPathValue,
299
+ risksFor: permissionPathRisks
300
+ },
301
+ {
302
+ key: "writePaths",
303
+ label: "writePaths",
304
+ sanitize: sanitizePermissionPathValue,
305
+ risksFor: permissionPathRisks
306
+ }
307
+ ]), summarizeFileSystemEntries(permissions.fileSystem, risks)].filter((summary) => Boolean(summary));
308
+ fileSystemSummary = summaries.length > 0 ? summaries.join("; ") : void 0;
309
+ }
273
310
  if (risks.size > 0) lines.push(`High-risk targets: ${[...risks].join(", ")}`);
274
311
  if (networkSummary) lines.push(`Network ${networkSummary}`);
275
312
  if (fileSystemSummary) lines.push(`File system ${fileSystemSummary}`);
276
313
  return lines;
277
314
  }
315
+ function summarizeNetworkEnabledPermission(permission, risks) {
316
+ const enabled = permission.enabled;
317
+ if (typeof enabled !== "boolean") return;
318
+ if (enabled) risks.add("network access");
319
+ return `enabled: ${enabled}`;
320
+ }
321
+ function summarizeFileSystemEntries(permission, risks) {
322
+ const entries = permission.entries;
323
+ if (!Array.isArray(entries)) return;
324
+ const samples = [];
325
+ let count = 0;
326
+ for (const entry of entries) {
327
+ const item = isJsonObject(entry) ? entry : void 0;
328
+ const path = typeof item?.path === "string" ? item.path.trim() : "";
329
+ const access = typeof item?.access === "string" ? item.access.trim() : "";
330
+ if (!path || !access) continue;
331
+ count += 1;
332
+ if (access !== "none") for (const risk of permissionPathRisks(path)) risks.add(risk);
333
+ if (samples.length < PERMISSION_SAMPLE_LIMIT) samples.push(`${sanitizePermissionScalar(access)} ${sanitizePermissionPathValue(path)}`);
334
+ }
335
+ if (count === 0) return;
336
+ const remaining = count - samples.length;
337
+ const remainderSuffix = remaining > 0 ? ` (+${remaining} more)` : "";
338
+ return `entries: ${samples.join(", ")}${remainderSuffix}`;
339
+ }
278
340
  function summarizePermissionRecord(permission, risks, descriptors) {
279
341
  const details = [];
280
342
  for (const descriptor of descriptors) {
@@ -293,6 +355,32 @@ function summarizePermissionArray(record, descriptor, risks) {
293
355
  const remainderSuffix = remaining > 0 ? ` (+${remaining} more)` : "";
294
356
  return `${descriptor.label}: ${sampleValues.join(", ")}${remainderSuffix}`;
295
357
  }
358
+ function summarizeStringArray(value, label, sanitize) {
359
+ if (!Array.isArray(value)) return;
360
+ const values = value.filter((entry) => typeof entry === "string").map((entry) => sanitize(entry)).filter(Boolean);
361
+ if (values.length === 0) return;
362
+ const samples = values.slice(0, PERMISSION_SAMPLE_LIMIT);
363
+ const remaining = values.length - samples.length;
364
+ const remainderSuffix = remaining > 0 ? ` (+${remaining} more)` : "";
365
+ return `${label}: ${samples.join(", ")}${remainderSuffix}`;
366
+ }
367
+ function summarizeNetworkPolicyAmendments(value) {
368
+ if (!Array.isArray(value)) return;
369
+ const samples = [];
370
+ let count = 0;
371
+ for (const entry of value) {
372
+ const amendment = isJsonObject(entry) ? entry : void 0;
373
+ const host = typeof amendment?.host === "string" ? amendment.host : "";
374
+ const action = typeof amendment?.action === "string" ? amendment.action : "";
375
+ if (!host || !action) continue;
376
+ count += 1;
377
+ if (samples.length < PERMISSION_SAMPLE_LIMIT) samples.push(`${sanitizePermissionScalar(action)} ${sanitizePermissionHostValue(host)}`);
378
+ }
379
+ if (count === 0) return;
380
+ const remaining = count - samples.length;
381
+ const remainderSuffix = remaining > 0 ? ` (+${remaining} more)` : "";
382
+ return `Proposed network policy: ${samples.join(", ")}${remainderSuffix}`;
383
+ }
296
384
  function readStringArray(record, key) {
297
385
  const value = record[key];
298
386
  return Array.isArray(value) ? value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean) : [];
@@ -433,7 +521,7 @@ function sanitizeApprovalPreview(source, maxLength) {
433
521
  const sanitized = sanitizeVisibleScalar(source.value.replace(DANGLING_TERMINAL_SEQUENCE_SUFFIX_RE$1, ""));
434
522
  if (!sanitized) return { omitted: true };
435
523
  return {
436
- text: truncate(sanitized, maxLength),
524
+ text: formatCodexDisplayText(truncate(sanitized, maxLength)),
437
525
  omitted: source.clipped
438
526
  };
439
527
  }
@@ -679,7 +767,8 @@ function sanitizeDisplayText(value) {
679
767
  const scanned = value.slice(0, DISPLAY_TEXT_SCAN_MAX_LENGTH);
680
768
  const clipped = value.length > DISPLAY_TEXT_SCAN_MAX_LENGTH;
681
769
  const sanitized = scanned.replace(ANSI_OSC_SEQUENCE_RE, "").replace(ANSI_CONTROL_SEQUENCE_RE, "").replace(DANGLING_TERMINAL_SEQUENCE_SUFFIX_RE, "").replace(INVISIBLE_FORMATTING_CONTROL_RE, " ").replace(CONTROL_CHARACTER_RE, " ").replace(/\s+/g, " ").trim();
682
- return clipped ? `${sanitized}...` : sanitized;
770
+ const escaped = sanitized ? formatCodexDisplayText(sanitized) : "";
771
+ return clipped && escaped ? `${escaped}...` : escaped;
683
772
  }
684
773
  function truncateDisplayText(value, maxLength) {
685
774
  return value.length <= maxLength ? value : `${value.slice(0, Math.max(0, maxLength - 3))}...`;
@@ -872,6 +961,110 @@ async function readCodexMirroredSessionHistoryMessages(sessionFile) {
872
961
  }
873
962
  }
874
963
  //#endregion
964
+ //#region extensions/codex/src/app-server/transcript-mirror.ts
965
+ const MIRROR_IDENTITY_META_KEY = "mirrorIdentity";
966
+ /**
967
+ * Tag a message with a stable logical identity for mirror dedupe. Callers
968
+ * should use a value that is invariant for the same logical message across
969
+ * re-emits (e.g. `${turnId}:prompt`, `${turnId}:assistant`) but distinct
970
+ * for genuinely-distinct messages (different turns, different kinds). When
971
+ * present this identity replaces the role/content fingerprint in the
972
+ * idempotency key, so the dedupe survives caller-scope rotation without
973
+ * collapsing distinct same-content turns.
974
+ */
975
+ function attachCodexMirrorIdentity(message, identity) {
976
+ const record = message;
977
+ const existing = record.__openclaw;
978
+ const baseMeta = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
979
+ return {
980
+ ...record,
981
+ __openclaw: {
982
+ ...baseMeta,
983
+ [MIRROR_IDENTITY_META_KEY]: identity
984
+ }
985
+ };
986
+ }
987
+ function readMirrorIdentity(message) {
988
+ const meta = message.__openclaw;
989
+ if (!meta || typeof meta !== "object" || Array.isArray(meta)) return;
990
+ const id = meta[MIRROR_IDENTITY_META_KEY];
991
+ return typeof id === "string" && id.length > 0 ? id : void 0;
992
+ }
993
+ function fingerprintMirrorMessageContent(message) {
994
+ const payload = JSON.stringify({
995
+ role: message.role,
996
+ content: message.content
997
+ });
998
+ return createHash("sha256").update(payload).digest("hex").slice(0, 16);
999
+ }
1000
+ function buildMirrorDedupeIdentity(message) {
1001
+ const explicit = readMirrorIdentity(message);
1002
+ if (explicit) return explicit;
1003
+ return `${message.role}:${fingerprintMirrorMessageContent(message)}`;
1004
+ }
1005
+ async function mirrorCodexAppServerTranscript(params) {
1006
+ const messages = params.messages.filter((message) => message.role === "user" || message.role === "assistant");
1007
+ if (messages.length === 0) return;
1008
+ const lock = await acquireSessionWriteLock({
1009
+ sessionFile: params.sessionFile,
1010
+ timeoutMs: resolveSessionWriteLockAcquireTimeoutMs(params.config)
1011
+ });
1012
+ try {
1013
+ const existingIdempotencyKeys = await readTranscriptIdempotencyKeys(params.sessionFile);
1014
+ for (const message of messages) {
1015
+ const dedupeIdentity = buildMirrorDedupeIdentity(message);
1016
+ const idempotencyKey = params.idempotencyScope ? `${params.idempotencyScope}:${dedupeIdentity}` : void 0;
1017
+ if (idempotencyKey && existingIdempotencyKeys.has(idempotencyKey)) continue;
1018
+ const nextMessage = runAgentHarnessBeforeMessageWriteHook({
1019
+ message: {
1020
+ ...message,
1021
+ ...idempotencyKey ? { idempotencyKey } : {}
1022
+ },
1023
+ agentId: params.agentId,
1024
+ sessionKey: params.sessionKey
1025
+ });
1026
+ if (!nextMessage) continue;
1027
+ const messageToAppend = idempotencyKey ? {
1028
+ ...nextMessage,
1029
+ idempotencyKey
1030
+ } : nextMessage;
1031
+ await appendSessionTranscriptMessage({
1032
+ transcriptPath: params.sessionFile,
1033
+ message: messageToAppend,
1034
+ config: params.config
1035
+ });
1036
+ if (idempotencyKey) existingIdempotencyKeys.add(idempotencyKey);
1037
+ }
1038
+ } finally {
1039
+ await lock.release();
1040
+ }
1041
+ if (params.sessionKey) emitSessionTranscriptUpdate({
1042
+ sessionFile: params.sessionFile,
1043
+ sessionKey: params.sessionKey
1044
+ });
1045
+ else emitSessionTranscriptUpdate(params.sessionFile);
1046
+ }
1047
+ async function readTranscriptIdempotencyKeys(sessionFile) {
1048
+ const keys = /* @__PURE__ */ new Set();
1049
+ let raw;
1050
+ try {
1051
+ raw = await fs.readFile(sessionFile, "utf8");
1052
+ } catch (error) {
1053
+ if (error.code !== "ENOENT") throw error;
1054
+ return keys;
1055
+ }
1056
+ for (const line of raw.split(/\r?\n/)) {
1057
+ if (!line.trim()) continue;
1058
+ try {
1059
+ const parsed = JSON.parse(line);
1060
+ if (typeof parsed.message?.idempotencyKey === "string") keys.add(parsed.message.idempotencyKey);
1061
+ } catch {
1062
+ continue;
1063
+ }
1064
+ }
1065
+ return keys;
1066
+ }
1067
+ //#endregion
875
1068
  //#region extensions/codex/src/app-server/event-projector.ts
876
1069
  const ZERO_USAGE = {
877
1070
  input: 0,
@@ -930,6 +1123,11 @@ var CodexAppServerEventProjector = class {
930
1123
  async handleNotification(notification) {
931
1124
  const params = isJsonObject(notification.params) ? notification.params : void 0;
932
1125
  if (!params) return;
1126
+ if (notification.method === "account/rateLimits/updated") {
1127
+ this.latestRateLimits = params;
1128
+ rememberCodexRateLimits(params);
1129
+ return;
1130
+ }
933
1131
  if (isHookNotificationMethod(notification.method)) {
934
1132
  if (!this.isHookNotificationForCurrentThread(params)) return;
935
1133
  } else if (!this.isNotificationForTurn(params)) return;
@@ -978,7 +1176,7 @@ var CodexAppServerEventProjector = class {
978
1176
  break;
979
1177
  case "error":
980
1178
  if (readBooleanAlias(params, ["willRetry", "will_retry"]) === true) break;
981
- this.promptError = readCodexErrorNotificationMessage(params) ?? "codex app-server error";
1179
+ this.promptError = this.formatCodexErrorMessage(params) ?? "codex app-server error";
982
1180
  this.promptErrorSource = "prompt";
983
1181
  break;
984
1182
  default: break;
@@ -989,14 +1187,15 @@ var CodexAppServerEventProjector = class {
989
1187
  const reasoningText = collectTextValues(this.reasoningTextByItem).join("\n\n");
990
1188
  const planText = collectTextValues(this.planTextByItem).join("\n\n");
991
1189
  const lastAssistant = assistantTexts.length > 0 ? this.createAssistantMessage(assistantTexts.join("\n\n")) : void 0;
992
- const messagesSnapshot = [{
1190
+ const turnId = this.turnId;
1191
+ const messagesSnapshot = [attachCodexMirrorIdentity({
993
1192
  role: "user",
994
1193
  content: this.params.prompt,
995
1194
  timestamp: Date.now()
996
- }];
997
- if (reasoningText) messagesSnapshot.push(this.createAssistantMirrorMessage("Codex reasoning", reasoningText));
998
- if (planText) messagesSnapshot.push(this.createAssistantMirrorMessage("Codex plan", planText));
999
- if (lastAssistant) messagesSnapshot.push(lastAssistant);
1195
+ }, `${turnId}:prompt`)];
1196
+ if (reasoningText) messagesSnapshot.push(attachCodexMirrorIdentity(this.createAssistantMirrorMessage("Codex reasoning", reasoningText), `${turnId}:reasoning`));
1197
+ if (planText) messagesSnapshot.push(attachCodexMirrorIdentity(this.createAssistantMirrorMessage("Codex plan", planText), `${turnId}:plan`));
1198
+ if (lastAssistant) messagesSnapshot.push(attachCodexMirrorIdentity(lastAssistant, `${turnId}:assistant`));
1000
1199
  const turnFailed = this.completedTurn?.status === "failed";
1001
1200
  const turnInterrupted = this.completedTurn?.status === "interrupted";
1002
1201
  const promptError = this.promptError ?? (turnFailed ? this.completedTurn?.error?.message ?? "codex app-server turn failed" : null);
@@ -1267,7 +1466,11 @@ var CodexAppServerEventProjector = class {
1267
1466
  this.completedTurn = turn;
1268
1467
  if (turn.status === "interrupted") this.aborted = true;
1269
1468
  if (turn.status === "failed") {
1270
- this.promptError = turn.error?.message ?? "codex app-server turn failed";
1469
+ this.promptError = formatCodexUsageLimitErrorMessage({
1470
+ message: turn.error?.message,
1471
+ codexErrorInfo: turn.error?.codexErrorInfo,
1472
+ rateLimits: this.latestRateLimits ?? readRecentCodexRateLimits()
1473
+ }) ?? turn.error?.message ?? "codex app-server turn failed";
1271
1474
  this.promptErrorSource = "prompt";
1272
1475
  }
1273
1476
  for (const item of turn.items ?? []) {
@@ -1354,6 +1557,7 @@ var CodexAppServerEventProjector = class {
1354
1557
  if (!item) return;
1355
1558
  const kind = itemKind(item);
1356
1559
  if (!kind) return;
1560
+ const meta = itemMeta(item, this.toolProgressDetailMode());
1357
1561
  this.emitAgentEvent({
1358
1562
  stream: "item",
1359
1563
  data: {
@@ -1363,7 +1567,7 @@ var CodexAppServerEventProjector = class {
1363
1567
  title: itemTitle(item),
1364
1568
  status: params.phase === "start" ? "running" : itemStatus(item),
1365
1569
  ...itemName(item) ? { name: itemName(item) } : {},
1366
- ...itemMeta(item) ? { meta: itemMeta(item) } : {}
1570
+ ...meta ? { meta } : {}
1367
1571
  }
1368
1572
  });
1369
1573
  }
@@ -1374,7 +1578,7 @@ var CodexAppServerEventProjector = class {
1374
1578
  const toolName = itemName(item);
1375
1579
  if (!toolName) return;
1376
1580
  this.toolResultSummaryItemIds.add(itemId);
1377
- const meta = itemMeta(item);
1581
+ const meta = itemMeta(item, this.toolProgressDetailMode());
1378
1582
  this.emitToolResultMessage({
1379
1583
  itemId,
1380
1584
  text: formatToolSummary(toolName, meta)
@@ -1390,7 +1594,7 @@ var CodexAppServerEventProjector = class {
1390
1594
  if (!toolName || !output) return;
1391
1595
  this.emitToolResultMessage({
1392
1596
  itemId,
1393
- text: formatToolOutput(toolName, itemMeta(item), output),
1597
+ text: formatToolOutput(toolName, itemMeta(item, this.toolProgressDetailMode()), output),
1394
1598
  finalOutput: true
1395
1599
  });
1396
1600
  }
@@ -1406,15 +1610,27 @@ var CodexAppServerEventProjector = class {
1406
1610
  shouldEmitToolOutput() {
1407
1611
  return typeof this.params.shouldEmitToolOutput === "function" ? this.params.shouldEmitToolOutput() : this.params.verboseLevel === "full";
1408
1612
  }
1613
+ toolProgressDetailMode() {
1614
+ return this.params.toolProgressDetail === "raw" ? "raw" : "explain";
1615
+ }
1409
1616
  recordToolMeta(item) {
1410
1617
  if (!item) return;
1411
1618
  const toolName = itemName(item);
1412
1619
  if (!toolName) return;
1620
+ const meta = itemMeta(item, this.toolProgressDetailMode());
1413
1621
  this.toolMetas.set(item.id, {
1414
1622
  toolName,
1415
- ...itemMeta(item) ? { meta: itemMeta(item) } : {}
1623
+ ...meta ? { meta } : {}
1416
1624
  });
1417
1625
  }
1626
+ formatCodexErrorMessage(params) {
1627
+ const error = isJsonObject(params.error) ? params.error : void 0;
1628
+ return formatCodexUsageLimitErrorMessage({
1629
+ message: error ? readString$2(error, "message") : void 0,
1630
+ codexErrorInfo: error?.codexErrorInfo,
1631
+ rateLimits: this.latestRateLimits ?? readRecentCodexRateLimits()
1632
+ }) ?? readCodexErrorNotificationMessage(params);
1633
+ }
1418
1634
  emitAgentEvent(event) {
1419
1635
  try {
1420
1636
  emitAgentEvent({
@@ -1653,14 +1869,14 @@ function itemName(item) {
1653
1869
  if (item.type === "fileChange") return "apply_patch";
1654
1870
  if (item.type === "webSearch") return "web_search";
1655
1871
  }
1656
- function itemMeta(item) {
1872
+ function itemMeta(item, detailMode = "explain") {
1657
1873
  if (item.type === "commandExecution" && typeof item.command === "string") return inferToolMetaFromArgs("exec", {
1658
1874
  command: item.command,
1659
1875
  cwd: typeof item.cwd === "string" ? item.cwd : void 0
1660
- });
1876
+ }, { detailMode });
1661
1877
  if (item.type === "webSearch" && typeof item.query === "string") return item.query;
1662
1878
  const toolName = itemName(item);
1663
- if ((item.type === "dynamicToolCall" || item.type === "mcpToolCall") && toolName) return inferToolMetaFromArgs(toolName, item.arguments);
1879
+ if ((item.type === "dynamicToolCall" || item.type === "mcpToolCall") && toolName) return inferToolMetaFromArgs(toolName, item.arguments, { detailMode });
1664
1880
  }
1665
1881
  function itemOutputText(item) {
1666
1882
  if (item.type === "commandExecution") return item.aggregatedOutput?.trim() || void 0;
@@ -2014,69 +2230,6 @@ function normalizeCodexTrajectoryError(value) {
2014
2230
  }
2015
2231
  }
2016
2232
  //#endregion
2017
- //#region extensions/codex/src/app-server/transcript-mirror.ts
2018
- async function mirrorCodexAppServerTranscript(params) {
2019
- const messages = params.messages.filter((message) => message.role === "user" || message.role === "assistant");
2020
- if (messages.length === 0) return;
2021
- const lock = await acquireSessionWriteLock({
2022
- sessionFile: params.sessionFile,
2023
- timeoutMs: resolveSessionWriteLockAcquireTimeoutMs(params.config)
2024
- });
2025
- try {
2026
- const existingIdempotencyKeys = await readTranscriptIdempotencyKeys(params.sessionFile);
2027
- for (const [index, message] of messages.entries()) {
2028
- const idempotencyKey = params.idempotencyScope ? `${params.idempotencyScope}:${message.role}:${index}` : void 0;
2029
- if (idempotencyKey && existingIdempotencyKeys.has(idempotencyKey)) continue;
2030
- const nextMessage = runAgentHarnessBeforeMessageWriteHook({
2031
- message: {
2032
- ...message,
2033
- ...idempotencyKey ? { idempotencyKey } : {}
2034
- },
2035
- agentId: params.agentId,
2036
- sessionKey: params.sessionKey
2037
- });
2038
- if (!nextMessage) continue;
2039
- const messageToAppend = idempotencyKey ? {
2040
- ...nextMessage,
2041
- idempotencyKey
2042
- } : nextMessage;
2043
- await appendSessionTranscriptMessage({
2044
- transcriptPath: params.sessionFile,
2045
- message: messageToAppend,
2046
- config: params.config
2047
- });
2048
- if (idempotencyKey) existingIdempotencyKeys.add(idempotencyKey);
2049
- }
2050
- } finally {
2051
- await lock.release();
2052
- }
2053
- if (params.sessionKey) emitSessionTranscriptUpdate({
2054
- sessionFile: params.sessionFile,
2055
- sessionKey: params.sessionKey
2056
- });
2057
- else emitSessionTranscriptUpdate(params.sessionFile);
2058
- }
2059
- async function readTranscriptIdempotencyKeys(sessionFile) {
2060
- const keys = /* @__PURE__ */ new Set();
2061
- let raw;
2062
- try {
2063
- raw = await fs.readFile(sessionFile, "utf8");
2064
- } catch (error) {
2065
- if (error.code !== "ENOENT") throw error;
2066
- return keys;
2067
- }
2068
- for (const line of raw.split(/\r?\n/)) {
2069
- if (!line.trim()) continue;
2070
- try {
2071
- const parsed = JSON.parse(line);
2072
- if (typeof parsed.message?.idempotencyKey === "string") keys.add(parsed.message.idempotencyKey);
2073
- } catch {
2074
- continue;
2075
- }
2076
- }
2077
- return keys;
2078
- }
2079
- //#endregion
2080
2233
  //#region extensions/codex/src/app-server/user-input-bridge.ts
2081
2234
  function createCodexUserInputBridge(params) {
2082
2235
  let pending;
@@ -2092,6 +2245,7 @@ function createCodexUserInputBridge(params) {
2092
2245
  const requestParams = readUserInputParams(request.params);
2093
2246
  if (!requestParams) return;
2094
2247
  if (requestParams.threadId !== params.threadId || requestParams.turnId !== params.turnId) return;
2248
+ if (requestParams.questions.length === 0) return emptyUserInputResponse();
2095
2249
  resolvePending(emptyUserInputResponse());
2096
2250
  return new Promise((resolve) => {
2097
2251
  const abortListener = () => resolvePending(emptyUserInputResponse());
@@ -2186,11 +2340,11 @@ async function deliverUserInputPrompt(params, questions) {
2186
2340
  function formatUserInputPrompt(questions) {
2187
2341
  const lines = ["Codex needs input:"];
2188
2342
  questions.forEach((question, index) => {
2189
- if (questions.length > 1) lines.push("", `${index + 1}. ${question.header}`, question.question);
2190
- else lines.push("", question.header, question.question);
2343
+ if (questions.length > 1) lines.push("", `${index + 1}. ${formatCodexDisplayText(question.header)}`, formatCodexDisplayText(question.question));
2344
+ else lines.push("", formatCodexDisplayText(question.header), formatCodexDisplayText(question.question));
2191
2345
  if (question.isSecret) lines.push("This channel may show your reply to other participants.");
2192
2346
  question.options?.forEach((option, optionIndex) => {
2193
- lines.push(`${optionIndex + 1}. ${option.label}${option.description ? ` - ${option.description}` : ""}`);
2347
+ lines.push(`${optionIndex + 1}. ${formatCodexDisplayText(option.label)}${option.description ? ` - ${formatCodexDisplayText(option.description)}` : ""}`);
2194
2348
  });
2195
2349
  if (question.isOther) lines.push("Other: reply with your own answer.");
2196
2350
  });
@@ -2200,14 +2354,18 @@ function buildUserInputResponse(questions, inputText) {
2200
2354
  const answers = {};
2201
2355
  if (questions.length === 1) {
2202
2356
  const question = questions[0];
2203
- if (question) answers[question.id] = { answers: [normalizeAnswer(inputText, question)] };
2357
+ if (question) {
2358
+ const answer = normalizeAnswer(inputText, question);
2359
+ answers[question.id] = { answers: answer ? [answer] : [] };
2360
+ }
2204
2361
  return { answers };
2205
2362
  }
2206
2363
  const keyed = parseKeyedAnswers(inputText);
2207
2364
  const fallbackLines = inputText.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
2208
2365
  questions.forEach((question, index) => {
2209
2366
  const answer = keyed.get(question.id.toLowerCase()) ?? keyed.get(question.header.toLowerCase()) ?? keyed.get(question.question.toLowerCase()) ?? keyed.get(String(index + 1)) ?? fallbackLines[index] ?? "";
2210
- answers[question.id] = { answers: answer ? [normalizeAnswer(answer, question)] : [] };
2367
+ const normalized = answer ? normalizeAnswer(answer, question) : void 0;
2368
+ answers[question.id] = { answers: normalized ? [normalized] : [] };
2211
2369
  });
2212
2370
  return { answers };
2213
2371
  }
@@ -2217,7 +2375,10 @@ function normalizeAnswer(answer, question) {
2217
2375
  const optionIndex = /^\d+$/.test(trimmed) ? Number(trimmed) - 1 : -1;
2218
2376
  const indexed = optionIndex >= 0 ? options[optionIndex] : void 0;
2219
2377
  if (indexed) return indexed.label;
2220
- return options.find((option) => option.label.toLowerCase() === trimmed.toLowerCase())?.label ?? trimmed;
2378
+ const exact = options.find((option) => option.label.toLowerCase() === trimmed.toLowerCase());
2379
+ if (exact) return exact.label;
2380
+ if (options.length > 0 && !question.isOther) return;
2381
+ return trimmed || void 0;
2221
2382
  }
2222
2383
  function parseKeyedAnswers(inputText) {
2223
2384
  const answers = /* @__PURE__ */ new Map();
@@ -2408,7 +2569,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
2408
2569
  const appServer = resolveCodexAppServerRuntimeOptions({ pluginConfig });
2409
2570
  const resolvedWorkspace = resolveUserPath(params.workspaceDir);
2410
2571
  await fs.mkdir(resolvedWorkspace, { recursive: true });
2411
- const sandboxSessionKey = params.sessionKey?.trim() || params.sessionId;
2572
+ const sandboxSessionKey = params.sandboxSessionKey?.trim() || params.sessionKey?.trim() || params.sessionId;
2412
2573
  const sandbox = await resolveSandboxContext({
2413
2574
  config: params.config,
2414
2575
  sessionKey: sandboxSessionKey,
@@ -2463,6 +2624,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
2463
2624
  signal: runAbortController.signal,
2464
2625
  hookContext: {
2465
2626
  agentId: sessionAgentId,
2627
+ config: params.config,
2466
2628
  sessionId: params.sessionId,
2467
2629
  sessionKey: sandboxSessionKey,
2468
2630
  runId: params.runId
@@ -2528,6 +2690,19 @@ async function runCodexAppServerAttempt(params, options = {}) {
2528
2690
  } catch (assembleErr) {
2529
2691
  embeddedAgentLog.warn("context engine assemble failed; using Codex baseline prompt", { error: formatErrorMessage(assembleErr) });
2530
2692
  }
2693
+ else if (shouldProjectMirroredHistoryForCodexStart({
2694
+ startupBinding,
2695
+ dynamicToolsFingerprint: codexDynamicToolsFingerprint(toolBridge.specs),
2696
+ historyMessages
2697
+ })) {
2698
+ const projection = projectContextEngineAssemblyForCodex({
2699
+ assembledMessages: historyMessages,
2700
+ originalHistoryMessages: historyMessages,
2701
+ prompt: params.prompt
2702
+ });
2703
+ promptText = projection.promptText;
2704
+ prePromptMessageCount = projection.prePromptMessageCount;
2705
+ }
2531
2706
  const promptBuild = await resolveAgentHarnessBeforePromptBuildResult({
2532
2707
  prompt: promptText,
2533
2708
  developerInstructions,
@@ -2563,6 +2738,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
2563
2738
  agentId: sessionAgentId,
2564
2739
  sessionId: params.sessionId,
2565
2740
  sessionKey: sandboxSessionKey,
2741
+ config: params.config,
2566
2742
  runId: params.runId,
2567
2743
  signal: runAbortController.signal
2568
2744
  });
@@ -2578,7 +2754,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
2578
2754
  operation: async () => {
2579
2755
  let attemptedClient;
2580
2756
  const startupAttempt = async () => {
2581
- const startupClient = await attemptClientFactory(appServer.start, startupAuthProfileId, agentDir);
2757
+ const startupClient = await attemptClientFactory(appServer.start, startupAuthProfileId, agentDir, params.config);
2582
2758
  attemptedClient = startupClient;
2583
2759
  startupClientForCleanup = startupClient;
2584
2760
  await ensureCodexComputerUse({
@@ -2950,11 +3126,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
2950
3126
  signal: runAbortController.signal
2951
3127
  }));
2952
3128
  } catch (error) {
3129
+ const usageLimitError = formatCodexTurnStartUsageLimitError(error, pendingNotifications);
3130
+ const turnStartErrorMessage = usageLimitError ?? formatErrorMessage(error);
2953
3131
  emitCodexAppServerEvent(params, {
2954
3132
  stream: "codex_app_server.lifecycle",
2955
3133
  data: {
2956
3134
  phase: "turn_start_failed",
2957
- error: formatErrorMessage(error)
3135
+ error: turnStartErrorMessage
2958
3136
  }
2959
3137
  });
2960
3138
  trajectoryRecorder?.recordEvent("session.ended", {
@@ -2962,7 +3140,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
2962
3140
  threadId: thread.threadId,
2963
3141
  timedOut,
2964
3142
  aborted: runAbortController.signal.aborted,
2965
- promptError: normalizeCodexTrajectoryError(error)
3143
+ promptError: turnStartErrorMessage
2966
3144
  });
2967
3145
  trajectoryEndRecorded = true;
2968
3146
  runAgentHarnessLlmOutputHook({
@@ -2981,7 +3159,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
2981
3159
  event: {
2982
3160
  messages: turnStartFailureMessages,
2983
3161
  success: false,
2984
- error: formatErrorMessage(error),
3162
+ error: turnStartErrorMessage,
2985
3163
  durationMs: Date.now() - attemptStartedAt
2986
3164
  },
2987
3165
  ctx: hookContext
@@ -2999,6 +3177,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
2999
3177
  }
3000
3178
  });
3001
3179
  params.abortSignal?.removeEventListener("abort", abortFromUpstream);
3180
+ if (usageLimitError) throw new Error(usageLimitError, { cause: error });
3002
3181
  throw error;
3003
3182
  }
3004
3183
  turnId = turn.turn.id;
@@ -3265,6 +3444,7 @@ function createCodexNativeHookRelay(params) {
3265
3444
  ...params.agentId ? { agentId: params.agentId } : {},
3266
3445
  sessionId: params.sessionId,
3267
3446
  ...params.sessionKey ? { sessionKey: params.sessionKey } : {},
3447
+ ...params.config ? { config: params.config } : {},
3268
3448
  runId: params.runId,
3269
3449
  allowedEvents: params.options?.events ?? CODEX_NATIVE_HOOK_RELAY_EVENTS,
3270
3450
  ttlMs: params.options?.ttlMs,
@@ -3315,6 +3495,7 @@ async function buildDynamicTools(input) {
3315
3495
  senderIsOwner: params.senderIsOwner,
3316
3496
  allowGatewaySubagentBinding: params.allowGatewaySubagentBinding,
3317
3497
  sessionKey: input.sandboxSessionKey,
3498
+ runSessionKey: params.sessionKey && params.sessionKey !== input.sandboxSessionKey ? params.sessionKey : void 0,
3318
3499
  sessionId: params.sessionId,
3319
3500
  runId: params.runId,
3320
3501
  agentDir,
@@ -3371,6 +3552,14 @@ async function buildDynamicTools(input) {
3371
3552
  model: params.model
3372
3553
  });
3373
3554
  }
3555
+ function shouldProjectMirroredHistoryForCodexStart(params) {
3556
+ if (!params.historyMessages.some((message) => message.role === "user")) return false;
3557
+ if (!params.startupBinding?.threadId) return true;
3558
+ return !areCodexDynamicToolFingerprintsCompatible({
3559
+ previous: params.startupBinding.dynamicToolsFingerprint,
3560
+ next: params.dynamicToolsFingerprint
3561
+ });
3562
+ }
3374
3563
  async function withCodexStartupTimeout(params) {
3375
3564
  if (params.signal.aborted) throw new Error("codex app-server startup aborted");
3376
3565
  let timeout;
@@ -3410,6 +3599,48 @@ function resolveCodexTurnTerminalIdleTimeoutMs(value) {
3410
3599
  function readDynamicToolCallParams(value) {
3411
3600
  return readCodexDynamicToolCallParams(value);
3412
3601
  }
3602
+ function formatCodexTurnStartUsageLimitError(error, pendingNotifications) {
3603
+ const notificationError = readLatestCodexErrorNotification(pendingNotifications);
3604
+ const errorPayload = readCodexErrorPayload(error);
3605
+ return formatCodexUsageLimitErrorMessage({
3606
+ message: notificationError?.message ?? errorPayload.message ?? formatErrorMessage(error),
3607
+ codexErrorInfo: notificationError?.codexErrorInfo ?? errorPayload.codexErrorInfo,
3608
+ rateLimits: readLatestRateLimitNotificationPayload(pendingNotifications) ?? errorPayload.rateLimits ?? readRecentCodexRateLimits()
3609
+ });
3610
+ }
3611
+ function readLatestRateLimitNotificationPayload(notifications) {
3612
+ for (let index = notifications.length - 1; index >= 0; index -= 1) {
3613
+ const notification = notifications[index];
3614
+ if (notification?.method === "account/rateLimits/updated") {
3615
+ rememberCodexRateLimits(notification.params);
3616
+ return notification.params;
3617
+ }
3618
+ }
3619
+ }
3620
+ function readLatestCodexErrorNotification(notifications) {
3621
+ for (let index = notifications.length - 1; index >= 0; index -= 1) {
3622
+ const notification = notifications[index];
3623
+ if (notification?.method !== "error" || !isJsonObject(notification.params)) continue;
3624
+ const error = notification.params.error;
3625
+ if (!isJsonObject(error)) continue;
3626
+ return {
3627
+ message: readString(error, "message"),
3628
+ codexErrorInfo: error.codexErrorInfo
3629
+ };
3630
+ }
3631
+ }
3632
+ function readCodexErrorPayload(error) {
3633
+ const message = error instanceof Error ? error.message : void 0;
3634
+ if (!error || typeof error !== "object" || !("data" in error)) return { message };
3635
+ const data = error.data;
3636
+ if (!isJsonObject(data)) return { message };
3637
+ const nestedError = isJsonObject(data.error) ? data.error : data;
3638
+ return {
3639
+ message: readString(nestedError, "message") ?? message,
3640
+ codexErrorInfo: nestedError.codexErrorInfo,
3641
+ rateLimits: nestedError.rateLimits ?? data.rateLimits
3642
+ };
3643
+ }
3413
3644
  function isTurnNotification(value, threadId, turnId) {
3414
3645
  if (!isJsonObject(value)) return false;
3415
3646
  return readString(value, "threadId") === threadId && readNotificationTurnId(value) === turnId;
@@ -3512,7 +3743,7 @@ async function mirrorTranscriptBestEffort(params) {
3512
3743
  agentId: params.agentId,
3513
3744
  sessionKey: params.sessionKey,
3514
3745
  messages: params.result.messagesSnapshot,
3515
- idempotencyScope: `codex-app-server:${params.threadId}:${params.turnId}`,
3746
+ idempotencyScope: `codex-app-server:${params.threadId}`,
3516
3747
  config: params.params.config
3517
3748
  });
3518
3749
  } catch (error) {