@openclaw/codex 2026.5.25-beta.1 → 2026.5.26-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.
Files changed (26) hide show
  1. package/dist/{client-SrtuTTVa.js → client-BVK_jmHW.js} +1 -1
  2. package/dist/client-factory-fW4Hh9q_.js +15 -0
  3. package/dist/{command-handlers-CLtPT37L.js → command-handlers-hYTtzAdy.js} +8 -8
  4. package/dist/{compact-acCua7zr.js → compact-C_1JAB4C.js} +26 -3
  5. package/dist/{computer-use-CVfE2Vfb.js → computer-use-DSdj1hJY.js} +2 -2
  6. package/dist/{config-D6TUuyo2.js → config-0-UN67Qg.js} +2 -1
  7. package/dist/{conversation-binding-B-GevkEb.js → conversation-binding-OjYqDwhw.js} +114 -88
  8. package/dist/{dynamic-tools-BXAE1U1-.js → dynamic-tools-DlEefKNQ.js} +61 -20
  9. package/dist/harness.js +5 -5
  10. package/dist/index.js +10 -9
  11. package/dist/media-understanding-provider.js +7 -7
  12. package/dist/{models-CC2IusQs.js → models-_XwpqjR8.js} +5 -4
  13. package/dist/{notification-correlation-D7tJW3FW.js → notification-correlation-YINts3PA.js} +1 -1
  14. package/dist/{protocol-validators-_gKDcd0x.js → protocol-validators-DtjYmUw_.js} +188 -441
  15. package/dist/provider.js +2 -2
  16. package/dist/{request-BO0z3vA8.js → request-OaxhR46w.js} +5 -4
  17. package/dist/{run-attempt-BjoI3hUJ.js → run-attempt-D8Vxo-Jm.js} +497 -176
  18. package/dist/{session-binding-CwNOIDP2.js → session-binding-UFjjws4k.js} +1 -1
  19. package/dist/{shared-client-CZP1DjaN.js → shared-client-8kIrP817.js} +167 -17
  20. package/dist/{side-question-Cb4COOQ9.js → side-question-DU7en3_R.js} +12 -11
  21. package/dist/test-api.js +3 -3
  22. package/dist/{thread-lifecycle-CXMd9UgJ.js → thread-lifecycle-CUXQezJL.js} +110 -22
  23. package/dist/{vision-tools-CETeQgze.js → vision-tools-DqpLmF5H.js} +5 -3
  24. package/npm-shrinkwrap.json +35 -105
  25. package/package.json +6 -6
  26. package/dist/client-factory-A7DQkSLq.js +0 -9
@@ -1,19 +1,19 @@
1
- import { t as defaultCodexAppServerClientFactory } from "./client-factory-A7DQkSLq.js";
2
- import { c as readCodexPluginConfig, d as resolveCodexPluginsPolicy, f as shouldAutoApproveCodexAppServerApprovals, i as isCodexAppServerApprovalPolicyAllowedByRequirements, l as resolveCodexAppServerRuntimeOptions, o as isCodexSandboxExecServerEnabled, p as withMcpElicitationsApprovalPolicy, u as resolveCodexComputerUseConfig } from "./config-D6TUuyo2.js";
3
- import { a as readCodexDynamicToolCallParams, c as readCodexTurn, i as assertCodexTurnStartResponse } from "./protocol-validators-_gKDcd0x.js";
4
- import { a as isCodexAppServerConnectionClosedError, i as isCodexAppServerApprovalRequest, l as isJsonObject, r as compareCodexAppServerVersions, s as MIN_CODEX_SANDBOX_EXEC_SERVER_APP_SERVER_VERSION } from "./client-SrtuTTVa.js";
5
- import { D as resolveCodexContextEngineProjectionMaxChars, E as projectContextEngineAssemblyForCodex, O as resolveCodexContextEngineProjectionReserveTokens, S as defaultCodexAppInventoryCache, _ as mergeCodexThreadConfigs, a as buildDeveloperInstructions, c as buildTurnCollaborationMode, d as isContextEngineBindingCompatible, g as buildCodexPluginThreadConfigInputFingerprint, h as buildCodexPluginThreadConfig, i as buildContextEngineBinding, l as buildTurnStartParams, m as startOrResumeThread, n as areCodexDynamicToolFingerprintsCompatible, u as codexDynamicToolsFingerprint, v as shouldBuildCodexPluginThreadConfig, w as sanitizeCodexHistoryImagePayloads } from "./thread-lifecycle-CXMd9UgJ.js";
6
- import { i as readCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-CwNOIDP2.js";
7
- import { n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS } from "./request-BO0z3vA8.js";
8
- import { g as shouldRefreshCodexRateLimitsForUsageLimitMessage, h as resolveCodexUsageLimitResetAtMs, i as readCodexNotificationTurnId, m as formatCodexUsageLimitErrorMessage, n as isCodexNotificationForTurn, r as readCodexNotificationThreadId, s as formatCodexDisplayText, t as describeCodexNotificationCorrelation } from "./notification-correlation-D7tJW3FW.js";
9
- import { c as resolveCodexAppServerAuthAccountCacheKey, d as resolveCodexAppServerEnvApiKeyCacheKey, f as resolveCodexAppServerHomeDir, l as resolveCodexAppServerAuthProfileId, s as refreshCodexAppServerAuthTokens, t as clearSharedCodexAppServerClientIfCurrent, u as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-CZP1DjaN.js";
1
+ import { n as defaultLeasedCodexAppServerClientFactory } from "./client-factory-fW4Hh9q_.js";
2
+ import { c as readCodexPluginConfig, d as resolveCodexPluginsPolicy, f as shouldAutoApproveCodexAppServerApprovals, i as isCodexAppServerApprovalPolicyAllowedByRequirements, l as resolveCodexAppServerRuntimeOptions, o as isCodexSandboxExecServerEnabled, p as withMcpElicitationsApprovalPolicy, u as resolveCodexComputerUseConfig } from "./config-0-UN67Qg.js";
3
+ import { a as readCodexDynamicToolCallParams, c as readCodexTurn, i as assertCodexTurnStartResponse } from "./protocol-validators-DtjYmUw_.js";
4
+ import { a as isCodexAppServerConnectionClosedError, i as isCodexAppServerApprovalRequest, l as isJsonObject, r as compareCodexAppServerVersions, s as MIN_CODEX_SANDBOX_EXEC_SERVER_APP_SERVER_VERSION } from "./client-BVK_jmHW.js";
5
+ import { C as defaultCodexAppInventoryCache, D as projectContextEngineAssemblyForCodex, O as resolveCodexContextEngineProjectionMaxChars, T as sanitizeCodexHistoryImagePayloads, _ as buildCodexPluginThreadConfigInputFingerprint, a as buildDeveloperInstructions, c as buildTurnCollaborationMode, d as isContextEngineBindingCompatible, g as buildCodexPluginThreadConfig, h as isCodexAppServerProfilerEnabled, i as buildContextEngineBinding, k as resolveCodexContextEngineProjectionReserveTokens, l as buildTurnStartParams, m as startOrResumeThread, n as areCodexDynamicToolFingerprintsCompatible, u as codexDynamicToolsFingerprint, v as mergeCodexThreadConfigs, y as shouldBuildCodexPluginThreadConfig } from "./thread-lifecycle-CUXQezJL.js";
6
+ import { i as readCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-UFjjws4k.js";
7
+ import { n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS } from "./request-OaxhR46w.js";
8
+ import { g as shouldRefreshCodexRateLimitsForUsageLimitMessage, h as resolveCodexUsageLimitResetAtMs, i as readCodexNotificationTurnId, m as formatCodexUsageLimitErrorMessage, n as isCodexNotificationForTurn, r as readCodexNotificationThreadId, s as formatCodexDisplayText, t as describeCodexNotificationCorrelation } from "./notification-correlation-YINts3PA.js";
9
+ import { a as releaseLeasedSharedCodexAppServerClient, d as resolveCodexAppServerAuthProfileId, f as resolveCodexAppServerAuthProfileIdForAgent, l as refreshCodexAppServerAuthTokens, m as resolveCodexAppServerHomeDir, o as retireSharedCodexAppServerClientIfCurrent, p as resolveCodexAppServerFallbackApiKeyCacheKey, t as clearSharedCodexAppServerClientIfCurrent, u as resolveCodexAppServerAuthAccountCacheKey } from "./shared-client-8kIrP817.js";
10
10
  import { i as resolveCodexNativeExecutionPolicy } from "./sandbox-guard-CTnEWuor.js";
11
- import { a as resolveCodexDynamicToolsLoading, i as normalizeCodexDynamicToolName, n as filterCodexDynamicTools, r as isForcedPrivateQaCodexRuntime, t as createCodexDynamicToolBridge } from "./dynamic-tools-BXAE1U1-.js";
12
- import { a as handleCodexAppServerElicitationRequest, c as emitDynamicToolTerminalDiagnostic, i as buildCodexNativeHookRelayDisabledConfig, l as handleCodexAppServerApprovalRequest, n as CODEX_NATIVE_HOOK_RELAY_EVENTS, o as emitDynamicToolErrorDiagnostic, r as buildCodexNativeHookRelayConfig, s as emitDynamicToolStartedDiagnostic, t as filterToolsForVisionInputs } from "./vision-tools-CETeQgze.js";
13
- import { t as ensureCodexComputerUse } from "./computer-use-CVfE2Vfb.js";
11
+ import { a as resolveCodexDynamicToolsLoading, i as normalizeCodexDynamicToolName, n as filterCodexDynamicTools, r as isForcedPrivateQaCodexRuntime, t as createCodexDynamicToolBridge } from "./dynamic-tools-DlEefKNQ.js";
12
+ import { a as handleCodexAppServerElicitationRequest, c as emitDynamicToolTerminalDiagnostic, i as buildCodexNativeHookRelayDisabledConfig, l as handleCodexAppServerApprovalRequest, n as CODEX_NATIVE_HOOK_RELAY_EVENTS, o as emitDynamicToolErrorDiagnostic, r as buildCodexNativeHookRelayConfig, s as emitDynamicToolStartedDiagnostic, t as filterToolsForVisionInputs } from "./vision-tools-DqpLmF5H.js";
13
+ import { t as ensureCodexComputerUse } from "./computer-use-DSdj1hJY.js";
14
14
  import { n as rememberCodexRateLimits, t as readRecentCodexRateLimits } from "./rate-limit-cache-N66I-Rd7.js";
15
15
  import { createHash, randomUUID } from "node:crypto";
16
- import nodeFs from "node:fs";
16
+ import fsSync from "node:fs";
17
17
  import { asBoolean, asFiniteNumber, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
18
18
  import { CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, TOOL_PROGRESS_OUTPUT_MAX_CHARS, acquireSessionWriteLock, appendSessionTranscriptMessage, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentHarnessAgentEndHook, bootstrapHarnessContextEngine, buildAgentHookContextChannelFields, buildEmbeddedAttemptToolRunContext, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, embeddedAgentLog, emitAgentEvent, emitSessionTranscriptUpdate, finalizeHarnessContextEngineTurn, formatErrorMessage, formatToolAggregate, formatToolProgressOutput, getBeforeToolCallPolicyDiagnosticState, inferToolMetaFromArgs, isActiveHarnessContextEngine, isSubagentSessionKey, loadCodexBundleMcpThreadConfig, normalizeAgentRuntimeTools, normalizeUsage, registerNativeHookRelay, resolveAgentHarnessBeforePromptBuildResult, resolveAttemptSpawnWorkspaceDir, resolveBootstrapContextForRun, resolveContextEngineOwnerPluginId, resolveModelAuthMode, resolveSandboxContext, resolveSessionAgentIds, resolveSessionWriteLockOptions, resolveUserPath, runAgentCleanupStep, runAgentHarnessAfterCompactionHook, runAgentHarnessAfterToolCallHook, runAgentHarnessAgentEndHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessBeforeMessageWriteHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
19
19
  import { spawn } from "node:child_process";
@@ -22,10 +22,10 @@ import { WebSocketServer } from "ws";
22
22
  import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
23
23
  import fs from "node:fs/promises";
24
24
  import path, { posix } from "node:path";
25
+ import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent, emitTrustedDiagnosticEventWithPrivateData, freezeDiagnosticTraceContext, hasPendingInternalDiagnosticEvent, onInternalDiagnosticEvent, resolveDiagnosticModelContentCapturePolicy } from "openclaw/plugin-sdk/diagnostic-runtime";
25
26
  import { markAuthProfileBlockedUntil, resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
26
27
  import { isToolAllowed } from "openclaw/plugin-sdk/sandbox";
27
28
  import { appendRegularFile, pathExists } from "openclaw/plugin-sdk/security-runtime";
28
- import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent, emitTrustedDiagnosticEventWithPrivateData, freezeDiagnosticTraceContext, hasPendingInternalDiagnosticEvent, onInternalDiagnosticEvent, resolveDiagnosticModelContentCapturePolicy } from "openclaw/plugin-sdk/diagnostic-runtime";
29
29
  import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "@earendil-works/pi-coding-agent";
30
30
  import { createAgentHarnessTaskRuntime, deliverAgentHarnessTaskCompletion, isDurableAgentHarnessCompletionDelivery } from "openclaw/plugin-sdk/agent-harness-task-runtime";
31
31
  import { isIP } from "node:net";
@@ -108,7 +108,7 @@ function buildSenderLabel(params) {
108
108
  if (!params.senderId || label.includes(params.senderId)) return label;
109
109
  return `${label} (${params.senderId})`;
110
110
  }
111
- function buildCodexUserPromptMessage(params) {
111
+ function buildCodexUserPromptMessageFromPrepared(params, preparedUserMessage) {
112
112
  const senderId = normalizeOptionalString(params.senderId);
113
113
  const senderName = normalizeOptionalString(params.senderName);
114
114
  const senderUsername = normalizeOptionalString(params.senderUsername);
@@ -120,6 +120,18 @@ function buildCodexUserPromptMessage(params) {
120
120
  senderE164
121
121
  });
122
122
  const sourceChannel = normalizeOptionalString(params.inputProvenance?.sourceChannel ?? params.messageChannel ?? params.messageProvider);
123
+ if (preparedUserMessage) return {
124
+ role: "user",
125
+ timestamp: Date.now(),
126
+ ...params.inputProvenance ? { provenance: params.inputProvenance } : {},
127
+ ...sourceChannel ? { sourceChannel } : {},
128
+ ...senderId ? { senderId } : {},
129
+ ...senderName ? { senderName } : {},
130
+ ...senderUsername ? { senderUsername } : {},
131
+ ...senderE164 ? { senderE164 } : {},
132
+ ...senderLabel ? { senderLabel } : {},
133
+ ...preparedUserMessage
134
+ };
123
135
  return {
124
136
  role: "user",
125
137
  content: params.prompt,
@@ -133,6 +145,12 @@ function buildCodexUserPromptMessage(params) {
133
145
  ...senderLabel ? { senderLabel } : {}
134
146
  };
135
147
  }
148
+ function buildCodexUserPromptMessage(params) {
149
+ return buildCodexUserPromptMessageFromPrepared(params, params.userTurnTranscriptRecorder?.message);
150
+ }
151
+ async function buildResolvedCodexUserPromptMessage(params) {
152
+ return buildCodexUserPromptMessageFromPrepared(params, await params.userTurnTranscriptRecorder?.resolveMessage() ?? params.userTurnTranscriptRecorder?.message);
153
+ }
136
154
  /**
137
155
  * Tag a message with a stable logical identity for mirror dedupe. Callers
138
156
  * should use a value that is invariant for the same logical message across
@@ -174,24 +192,30 @@ function buildMirrorDedupeIdentity(message) {
174
192
  }
175
193
  async function mirrorCodexAppServerTranscript(params) {
176
194
  const messages = params.messages.filter((message) => message.role === "user" || message.role === "assistant" || message.role === "toolResult");
177
- if (messages.length === 0) return;
195
+ if (messages.length === 0) return { userMessagesPresent: [] };
178
196
  const lock = await acquireSessionWriteLock({
179
197
  sessionFile: params.sessionFile,
180
198
  ...resolveSessionWriteLockOptions(params.config)
181
199
  });
182
200
  const appendedUpdates = [];
201
+ const userMessagesPresent = [];
183
202
  try {
184
203
  const mirrorState = await readTranscriptMirrorState(params.sessionFile);
185
204
  let nextMessageSeq = mirrorState.messageCount;
186
205
  for (const message of messages) {
187
206
  const dedupeIdentity = buildMirrorDedupeIdentity(message);
188
207
  const idempotencyKey = params.idempotencyScope ? `${params.idempotencyScope}:${dedupeIdentity}` : void 0;
189
- if (idempotencyKey && mirrorState.idempotencyKeys.has(idempotencyKey)) continue;
208
+ const transcriptMessage = {
209
+ ...message,
210
+ ...idempotencyKey ? { idempotencyKey } : {}
211
+ };
212
+ if (idempotencyKey && mirrorState.idempotencyKeys.has(idempotencyKey)) {
213
+ const persistedUserMessage = mirrorState.userMessagesByIdempotencyKey.get(idempotencyKey);
214
+ if (persistedUserMessage) userMessagesPresent.push(persistedUserMessage);
215
+ continue;
216
+ }
190
217
  const nextMessage = runAgentHarnessBeforeMessageWriteHook({
191
- message: {
192
- ...message,
193
- ...idempotencyKey ? { idempotencyKey } : {}
194
- },
218
+ message: transcriptMessage,
195
219
  agentId: params.agentId,
196
220
  sessionKey: params.sessionKey
197
221
  });
@@ -203,8 +227,13 @@ async function mirrorCodexAppServerTranscript(params) {
203
227
  const { messageId, message: appendedMessage } = await appendSessionTranscriptMessage({
204
228
  transcriptPath: params.sessionFile,
205
229
  message: messageToAppend,
230
+ idempotencyLookup: idempotencyKey ? "caller-checked" : "scan",
206
231
  config: params.config
207
232
  });
233
+ if (appendedMessage.role === "user") {
234
+ userMessagesPresent.push(appendedMessage);
235
+ if (idempotencyKey) mirrorState.userMessagesByIdempotencyKey.set(idempotencyKey, appendedMessage);
236
+ }
208
237
  nextMessageSeq += 1;
209
238
  appendedUpdates.push({
210
239
  messageId,
@@ -223,9 +252,11 @@ async function mirrorCodexAppServerTranscript(params) {
223
252
  messageId: update.messageId,
224
253
  messageSeq: update.messageSeq
225
254
  });
255
+ return { userMessagesPresent };
226
256
  }
227
257
  async function readTranscriptMirrorState(sessionFile) {
228
258
  const idempotencyKeys = /* @__PURE__ */ new Set();
259
+ const userMessagesByIdempotencyKey = /* @__PURE__ */ new Map();
229
260
  let messageCount = 0;
230
261
  let raw;
231
262
  try {
@@ -234,7 +265,8 @@ async function readTranscriptMirrorState(sessionFile) {
234
265
  if (error.code !== "ENOENT") throw error;
235
266
  return {
236
267
  idempotencyKeys,
237
- messageCount
268
+ messageCount,
269
+ userMessagesByIdempotencyKey
238
270
  };
239
271
  }
240
272
  for (const line of raw.split(/\r?\n/)) {
@@ -242,14 +274,18 @@ async function readTranscriptMirrorState(sessionFile) {
242
274
  try {
243
275
  const parsed = JSON.parse(line);
244
276
  if (parsed.type === "message") messageCount += 1;
245
- if (typeof parsed.message?.idempotencyKey === "string") idempotencyKeys.add(parsed.message.idempotencyKey);
277
+ if (typeof parsed.message?.idempotencyKey === "string") {
278
+ idempotencyKeys.add(parsed.message.idempotencyKey);
279
+ if (parsed.message.role === "user") userMessagesByIdempotencyKey.set(parsed.message.idempotencyKey, parsed.message);
280
+ }
246
281
  } catch {
247
282
  continue;
248
283
  }
249
284
  }
250
285
  return {
251
286
  idempotencyKeys,
252
- messageCount
287
+ messageCount,
288
+ userMessagesByIdempotencyKey
253
289
  };
254
290
  }
255
291
  //#endregion
@@ -3946,7 +3982,7 @@ const JWT_VALUE_RE = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]
3946
3982
  const COOKIE_PAIR_RE = /\b([A-Za-z][A-Za-z0-9_.-]{1,64})=([A-Za-z0-9+/._~%=-]{16,})(?=;|\s|$)/gu;
3947
3983
  const TRAJECTORY_RUNTIME_FILE_MAX_BYTES = 50 * 1024 * 1024;
3948
3984
  const TRAJECTORY_RUNTIME_EVENT_MAX_BYTES = 256 * 1024;
3949
- function resolveCodexTrajectoryPointerFlags(constants = nodeFs.constants) {
3985
+ function resolveCodexTrajectoryPointerFlags(constants = fsSync.constants) {
3950
3986
  const noFollow = constants.O_NOFOLLOW;
3951
3987
  return constants.O_CREAT | constants.O_TRUNC | constants.O_WRONLY | (typeof noFollow === "number" ? noFollow : 0);
3952
3988
  }
@@ -3980,23 +4016,23 @@ function writeTrajectoryPointerBestEffort(params) {
3980
4016
  const pointerPath = resolveTrajectoryPointerFilePath(params.sessionFile);
3981
4017
  try {
3982
4018
  const pointerDir = path.resolve(path.dirname(pointerPath));
3983
- if (nodeFs.lstatSync(pointerDir).isSymbolicLink()) return;
4019
+ if (fsSync.lstatSync(pointerDir).isSymbolicLink()) return;
3984
4020
  try {
3985
- if (nodeFs.lstatSync(pointerPath).isSymbolicLink()) return;
4021
+ if (fsSync.lstatSync(pointerPath).isSymbolicLink()) return;
3986
4022
  } catch (error) {
3987
4023
  if (error.code !== "ENOENT") return;
3988
4024
  }
3989
- const fd = nodeFs.openSync(pointerPath, resolveCodexTrajectoryPointerFlags(), 384);
4025
+ const fd = fsSync.openSync(pointerPath, resolveCodexTrajectoryPointerFlags(), 384);
3990
4026
  try {
3991
- nodeFs.writeFileSync(fd, `${JSON.stringify({
4027
+ fsSync.writeFileSync(fd, `${JSON.stringify({
3992
4028
  traceSchema: "openclaw-trajectory-pointer",
3993
4029
  schemaVersion: 1,
3994
4030
  sessionId: params.sessionId,
3995
4031
  runtimeFile: params.filePath
3996
4032
  }, null, 2)}\n`, "utf8");
3997
- nodeFs.fchmodSync(fd, 384);
4033
+ fsSync.fchmodSync(fd, 384);
3998
4034
  } finally {
3999
- nodeFs.closeSync(fd);
4035
+ fsSync.closeSync(fd);
4000
4036
  }
4001
4037
  } catch {}
4002
4038
  }
@@ -4312,7 +4348,7 @@ function readRequestId(record) {
4312
4348
  }
4313
4349
  //#endregion
4314
4350
  //#region extensions/codex/src/app-server/run-attempt.ts
4315
- const CODEX_DYNAMIC_TOOL_TIMEOUT_MS = 3e4;
4351
+ const CODEX_DYNAMIC_TOOL_TIMEOUT_MS = 9e4;
4316
4352
  const CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS = 6e5;
4317
4353
  const CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12e4;
4318
4354
  const CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS = 6e4;
@@ -4361,6 +4397,7 @@ const CODEX_BOOTSTRAP_CONTEXT_ORDER = new Map([
4361
4397
  ["memory.md", 60],
4362
4398
  ["heartbeat.md", 70]
4363
4399
  ]);
4400
+ const ensuredCodexWorkspaceDirs = /* @__PURE__ */ new Set();
4364
4401
  const pendingCodexNativeHookRelayUnregisters = /* @__PURE__ */ new Set();
4365
4402
  function scheduleCodexNativeHookRelayUnregister(params) {
4366
4403
  let pending;
@@ -4383,6 +4420,19 @@ function resolveCodexNativeHookRelayUnregisterGraceMs(hookTimeoutSec) {
4383
4420
  const hookTimeoutMs = typeof hookTimeoutSec === "number" && Number.isFinite(hookTimeoutSec) && hookTimeoutSec > 0 ? Math.ceil(hookTimeoutSec) * 1e3 : 0;
4384
4421
  return Math.max(CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS, hookTimeoutMs + CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS);
4385
4422
  }
4423
+ async function ensureCodexWorkspaceDirOnce(workspaceDir) {
4424
+ const normalized = path.resolve(workspaceDir);
4425
+ if (ensuredCodexWorkspaceDirs.has(normalized)) {
4426
+ try {
4427
+ if ((await fs.stat(normalized)).isDirectory()) return;
4428
+ } catch (error) {
4429
+ if ((typeof error === "object" && error ? error.code : void 0) !== "ENOENT") throw error;
4430
+ }
4431
+ ensuredCodexWorkspaceDirs.delete(normalized);
4432
+ }
4433
+ await fs.mkdir(normalized, { recursive: true });
4434
+ ensuredCodexWorkspaceDirs.add(normalized);
4435
+ }
4386
4436
  function emitCodexAppServerEvent(params, event) {
4387
4437
  try {
4388
4438
  emitAgentEvent({
@@ -4565,7 +4615,7 @@ function isCodexAppServerPolicyMode(value) {
4565
4615
  function isCodexAppServerApprovalPolicy(value) {
4566
4616
  return value === "never" || value === "on-request" || value === "on-failure" || value === "untrusted";
4567
4617
  }
4568
- const CODEX_APP_SERVER_NATIVE_THREAD_MAX_TOKENS = 7e4;
4618
+ const CODEX_APP_SERVER_NATIVE_THREAD_FALLBACK_MAX_TOKENS = 3e5;
4569
4619
  const CODEX_APP_SERVER_BYTE_UNITS = {
4570
4620
  b: 1,
4571
4621
  k: 1024,
@@ -4653,25 +4703,29 @@ async function readCodexSessionRecordForSessionFile(sessionFile) {
4653
4703
  };
4654
4704
  }
4655
4705
  }
4656
- async function readCodexAppServerRolloutTokenUsage(file) {
4706
+ async function readCodexAppServerRolloutTokenSnapshot(file) {
4657
4707
  let handle;
4658
4708
  try {
4659
4709
  handle = await fs.open(file, "r");
4660
4710
  } catch {
4661
4711
  return;
4662
4712
  }
4663
- let totalTokens;
4713
+ let snapshot;
4664
4714
  try {
4665
4715
  for await (const line of handle.readLines()) {
4666
- const lineTokens = readCodexAppServerRolloutTokenUsageLine(line);
4667
- if (lineTokens !== void 0) totalTokens = lineTokens;
4716
+ const lineSnapshot = readCodexAppServerRolloutTokenSnapshotLine(line);
4717
+ if (lineSnapshot !== void 0) {
4718
+ snapshot ??= {};
4719
+ if (lineSnapshot.totalTokens !== void 0) snapshot.totalTokens = lineSnapshot.totalTokens;
4720
+ if (lineSnapshot.modelContextWindow !== void 0) snapshot.modelContextWindow = lineSnapshot.modelContextWindow;
4721
+ }
4668
4722
  }
4669
4723
  } finally {
4670
4724
  await handle.close();
4671
4725
  }
4672
- return totalTokens;
4726
+ return snapshot;
4673
4727
  }
4674
- function readCodexAppServerRolloutTokenUsageLine(line) {
4728
+ function readCodexAppServerRolloutTokenSnapshotLine(line) {
4675
4729
  if (!line.trim()) return;
4676
4730
  try {
4677
4731
  const parsed = JSON.parse(line);
@@ -4680,11 +4734,20 @@ function readCodexAppServerRolloutTokenUsageLine(line) {
4680
4734
  if (!info) return;
4681
4735
  const usage = isJsonObject(info.last_token_usage) ? info.last_token_usage : isJsonObject(info.total_token_usage) ? info.total_token_usage : void 0;
4682
4736
  const value = usage?.total_tokens ?? usage?.totalTokens;
4683
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
4737
+ const totalTokens = typeof value === "number" && Number.isFinite(value) ? value : void 0;
4738
+ const windowValue = info.model_context_window ?? info.modelContextWindow;
4739
+ const modelContextWindow = typeof windowValue === "number" && Number.isFinite(windowValue) && windowValue > 0 ? Math.floor(windowValue) : void 0;
4740
+ const snapshot = {};
4741
+ if (totalTokens !== void 0) snapshot.totalTokens = totalTokens;
4742
+ if (modelContextWindow !== void 0) snapshot.modelContextWindow = modelContextWindow;
4743
+ return snapshot.totalTokens !== void 0 || snapshot.modelContextWindow !== void 0 ? snapshot : void 0;
4684
4744
  } catch {
4685
4745
  return;
4686
4746
  }
4687
4747
  }
4748
+ function resolveCodexAppServerNativeThreadTokenFuse(modelContextWindow) {
4749
+ return modelContextWindow ?? CODEX_APP_SERVER_NATIVE_THREAD_FALLBACK_MAX_TOKENS;
4750
+ }
4688
4751
  function utf8JsonByteLength(value) {
4689
4752
  try {
4690
4753
  return Buffer.byteLength(JSON.stringify(value), "utf8");
@@ -4697,10 +4760,22 @@ function maxFiniteNumber(values) {
4697
4760
  if (nums.length === 0) return;
4698
4761
  return Math.max(...nums);
4699
4762
  }
4763
+ function hasContextEngineThreadBootstrapProjection(binding) {
4764
+ return binding.contextEngine?.projection?.mode === "thread_bootstrap";
4765
+ }
4700
4766
  async function rotateOversizedCodexAppServerStartupBinding(params) {
4701
4767
  const binding = params.binding;
4702
4768
  if (!binding?.threadId) return binding;
4703
4769
  if (params.config?.agents?.defaults?.compaction?.truncateAfterCompaction !== true) return binding;
4770
+ if (params.contextEngineActive === true && hasContextEngineThreadBootstrapProjection(binding)) {
4771
+ embeddedAgentLog.debug("codex app-server deferring native transcript size guard for context-engine thread bootstrap", {
4772
+ threadId: binding.threadId,
4773
+ engineId: binding.contextEngine?.engineId,
4774
+ epoch: binding.contextEngine?.projection?.epoch,
4775
+ fingerprint: binding.contextEngine?.projection?.fingerprint
4776
+ });
4777
+ return binding;
4778
+ }
4704
4779
  const sessionRecord = await readCodexSessionRecordForSessionFile(params.sessionFile);
4705
4780
  const maxBytes = parseCodexAppServerByteLimit(params.config?.agents?.defaults?.compaction?.maxActiveTranscriptBytes);
4706
4781
  const rolloutFiles = await listCodexAppServerRolloutFilesForThread(params.agentDir, binding.threadId, params.codexHome);
@@ -4719,16 +4794,20 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
4719
4794
  return;
4720
4795
  }
4721
4796
  }
4722
- const nativeTokens = maxFiniteNumber(await Promise.all(rolloutFiles.map(async (file) => readCodexAppServerRolloutTokenUsage(file.path))));
4797
+ const nativeTokenSnapshots = await Promise.all(rolloutFiles.map(async (file) => readCodexAppServerRolloutTokenSnapshot(file.path)));
4798
+ const nativeTokens = maxFiniteNumber(nativeTokenSnapshots.map((snapshot) => snapshot?.totalTokens));
4799
+ const nativeModelContextWindow = maxFiniteNumber(nativeTokenSnapshots.map((snapshot) => snapshot?.modelContextWindow));
4800
+ const maxTokens = resolveCodexAppServerNativeThreadTokenFuse(nativeModelContextWindow);
4723
4801
  const sessionTokens = sessionRecord?.totalTokensFresh !== false && typeof sessionRecord?.totalTokens === "number" && Number.isFinite(sessionRecord.totalTokens) ? sessionRecord.totalTokens : void 0;
4724
4802
  const tokenCount = maxFiniteNumber([sessionTokens, nativeTokens]);
4725
- if (tokenCount !== void 0 && tokenCount >= CODEX_APP_SERVER_NATIVE_THREAD_MAX_TOKENS) {
4803
+ if (tokenCount !== void 0 && tokenCount >= maxTokens) {
4726
4804
  embeddedAgentLog.warn("codex app-server native transcript exceeded active token limit; starting a fresh thread", {
4727
4805
  threadId: binding.threadId,
4728
- maxTokens: CODEX_APP_SERVER_NATIVE_THREAD_MAX_TOKENS,
4806
+ maxTokens,
4729
4807
  sessionKey: sessionRecord?.sessionKey,
4730
4808
  sessionTokens,
4731
- nativeTokens
4809
+ nativeTokens,
4810
+ nativeModelContextWindow
4732
4811
  });
4733
4812
  await clearCodexAppServerBinding(params.sessionFile);
4734
4813
  return;
@@ -4747,6 +4826,7 @@ async function runCodexAgentEndHook(params, hookParams) {
4747
4826
  }
4748
4827
  async function runCodexAppServerAttempt(params, options = {}) {
4749
4828
  const attemptStartedAt = Date.now();
4829
+ const profilerEnabled = isCodexAppServerProfilerEnabled(params.config);
4750
4830
  const codexModelCallTrace = freezeDiagnosticTraceContext(createDiagnosticTraceContextFromActiveScope());
4751
4831
  const codexModelContentCapture = resolveDiagnosticModelContentCapturePolicy(params.config);
4752
4832
  const codexModelCallId = `${params.runId}:codex-model:1`;
@@ -4754,13 +4834,16 @@ async function runCodexAppServerAttempt(params, options = {}) {
4754
4834
  let codexModelCallStarted = false;
4755
4835
  let codexModelCallTerminalEmitted = false;
4756
4836
  let codexModelCallRequestPayloadBytes;
4757
- const attemptClientFactory = options.clientFactory ?? defaultCodexAppServerClientFactory;
4837
+ const preDynamicStartupStages = createCodexDynamicToolBuildStageTracker({ enabled: profilerEnabled });
4838
+ const attemptClientFactory = options.clientFactory ?? defaultLeasedCodexAppServerClientFactory;
4758
4839
  const pluginConfig = readCodexPluginConfig(options.pluginConfig);
4759
4840
  const computerUseConfig = resolveCodexComputerUseConfig({ pluginConfig });
4760
4841
  const configuredAppServer = resolveCodexAppServerRuntimeOptions({ pluginConfig });
4761
4842
  const beforeToolCallPolicy = getBeforeToolCallPolicyDiagnosticState();
4843
+ preDynamicStartupStages.mark("config");
4762
4844
  const resolvedWorkspace = resolveUserPath(params.workspaceDir);
4763
- await fs.mkdir(resolvedWorkspace, { recursive: true });
4845
+ await ensureCodexWorkspaceDirOnce(resolvedWorkspace);
4846
+ preDynamicStartupStages.mark("workspace");
4764
4847
  const sandboxSessionKey = params.sandboxSessionKey?.trim() || params.sessionKey?.trim() || params.sessionId;
4765
4848
  const contextSessionKey = params.sessionKey?.trim() || sandboxSessionKey;
4766
4849
  const sandbox = await resolveSandboxContext({
@@ -4768,8 +4851,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
4768
4851
  sessionKey: sandboxSessionKey,
4769
4852
  workspaceDir: resolvedWorkspace
4770
4853
  });
4854
+ preDynamicStartupStages.mark("sandbox");
4771
4855
  const effectiveWorkspace = sandbox?.enabled ? sandbox.workspaceAccess === "rw" ? resolvedWorkspace : sandbox.workspaceDir : resolvedWorkspace;
4772
- await fs.mkdir(effectiveWorkspace, { recursive: true });
4856
+ await ensureCodexWorkspaceDirOnce(effectiveWorkspace);
4857
+ preDynamicStartupStages.mark("effective-workspace");
4773
4858
  const appServer = resolveCodexAppServerForOpenClawToolPolicy({
4774
4859
  appServer: configuredAppServer,
4775
4860
  pluginConfig,
@@ -4783,11 +4868,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
4783
4868
  beforeToolCallHook: beforeToolCallPolicy.hasBeforeToolCallHook,
4784
4869
  trustedToolPolicies: beforeToolCallPolicy.trustedToolPolicies
4785
4870
  });
4871
+ preDynamicStartupStages.mark("app-server-policy");
4786
4872
  let pluginAppServer = appServer;
4787
4873
  const nativeHookRelayEvents = resolveCodexNativeHookRelayEvents({
4788
4874
  configuredEvents: options.nativeHookRelay?.events,
4789
4875
  appServer
4790
4876
  });
4877
+ preDynamicStartupStages.mark("native-hook-relay");
4791
4878
  const runAbortController = new AbortController();
4792
4879
  const abortFromUpstream = () => {
4793
4880
  runAbortController.abort(params.abortSignal?.reason ?? "upstream_abort");
@@ -4800,15 +4887,19 @@ async function runCodexAppServerAttempt(params, options = {}) {
4800
4887
  agentId: params.agentId
4801
4888
  });
4802
4889
  const agentDir = params.agentDir ?? resolveAgentDir$1(params.config ?? {}, sessionAgentId);
4890
+ preDynamicStartupStages.mark("session-agent");
4803
4891
  let startupBinding = await readCodexAppServerBinding(params.sessionFile);
4892
+ preDynamicStartupStages.mark("read-binding");
4804
4893
  const startupBindingAuthProfileId = startupBinding?.authProfileId;
4805
4894
  startupBinding = await rotateOversizedCodexAppServerStartupBinding({
4806
4895
  binding: startupBinding,
4807
4896
  sessionFile: params.sessionFile,
4808
4897
  agentDir,
4809
4898
  codexHome: appServer.start.env?.CODEX_HOME,
4810
- config: params.config
4899
+ config: params.config,
4900
+ contextEngineActive: isActiveHarnessContextEngine(params.contextEngine)
4811
4901
  });
4902
+ preDynamicStartupStages.mark("rotate-binding");
4812
4903
  const startupAuthProfileCandidate = params.runtimePlan?.auth.forwardedAuthProfileId ?? params.authProfileId ?? startupBinding?.authProfileId ?? startupBindingAuthProfileId;
4813
4904
  const startupAuthProfileId = params.authProfileStore ? resolveCodexAppServerAuthProfileId({
4814
4905
  authProfileId: startupAuthProfileCandidate,
@@ -4819,6 +4910,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
4819
4910
  agentDir,
4820
4911
  config: params.config
4821
4912
  });
4913
+ preDynamicStartupStages.mark("auth-profile");
4822
4914
  const runtimeParams = {
4823
4915
  ...params,
4824
4916
  sessionKey: contextSessionKey,
@@ -4837,12 +4929,14 @@ async function runCodexAppServerAttempt(params, options = {}) {
4837
4929
  agentDir,
4838
4930
  config: params.config
4839
4931
  });
4840
- const startupEnvApiKeyCacheKey = startupAuthProfileId ? void 0 : resolveCodexAppServerEnvApiKeyCacheKey({ startOptions: appServer.start });
4932
+ const startupEnvApiKeyCacheKey = startupAuthProfileId ? void 0 : resolveCodexAppServerFallbackApiKeyCacheKey({ startOptions: appServer.start });
4933
+ preDynamicStartupStages.mark("auth-cache");
4841
4934
  const nodeExecBlocksNativeExecution = isCodexNativeExecutionBlockedByNodeExecHost(params, {
4842
4935
  agentId: sessionAgentId,
4843
4936
  runtimeSessionKey: sandboxSessionKey,
4844
4937
  sandbox
4845
4938
  });
4939
+ preDynamicStartupStages.mark("native-exec-policy");
4846
4940
  const bundleMcpThreadConfig = await loadCodexBundleMcpThreadConfig({
4847
4941
  workspaceDir: effectiveWorkspace,
4848
4942
  cfg: params.config,
@@ -4850,12 +4944,14 @@ async function runCodexAppServerAttempt(params, options = {}) {
4850
4944
  disableTools: params.disableTools,
4851
4945
  toolsAllow: nodeExecBlocksNativeExecution ? [] : params.toolsAllow
4852
4946
  });
4947
+ preDynamicStartupStages.mark("bundle-mcp");
4853
4948
  const sandboxExecServerEnabled = isCodexSandboxExecServerEnabled(pluginConfig);
4854
4949
  const nativeToolSurfaceEnabled = shouldEnableCodexAppServerNativeToolSurface(params, sandbox, {
4855
4950
  agentId: sessionAgentId,
4856
4951
  runtimeSessionKey: sandboxSessionKey,
4857
4952
  sandboxExecServerEnabled
4858
4953
  });
4954
+ preDynamicStartupStages.mark("native-tool-surface");
4859
4955
  for (const diagnostic of bundleMcpThreadConfig.diagnostics) embeddedAgentLog.warn(`bundle-mcp: ${diagnostic.pluginId}: ${diagnostic.message}`);
4860
4956
  const activeContextEngine = isActiveHarnessContextEngine(params.contextEngine) ? params.contextEngine : void 0;
4861
4957
  if (activeContextEngine) assertContextEngineHostSupport({
@@ -4864,6 +4960,18 @@ async function runCodexAppServerAttempt(params, options = {}) {
4864
4960
  host: CODEX_APP_SERVER_CONTEXT_ENGINE_HOST
4865
4961
  });
4866
4962
  const hookChannelId = resolveCodexAppServerHookChannelId(params, sandboxSessionKey);
4963
+ preDynamicStartupStages.mark("context-engine-support");
4964
+ const preDynamicSummary = preDynamicStartupStages.snapshot();
4965
+ if (shouldWarnCodexDynamicToolBuildStageSummary(preDynamicSummary)) embeddedAgentLog.warn(`codex app-server pre-dynamic startup timings runId=${params.runId} sessionId=${params.sessionId} totalMs=${preDynamicSummary.totalMs} stages=${formatCodexDynamicToolBuildStageSummary(preDynamicSummary)}`, {
4966
+ runId: params.runId,
4967
+ sessionId: params.sessionId,
4968
+ totalMs: preDynamicSummary.totalMs,
4969
+ stages: preDynamicSummary.stages,
4970
+ hasStartupBinding: Boolean(startupBinding?.threadId),
4971
+ startupAuthProfileId: startupAuthProfileId ?? null,
4972
+ bundleMcpDiagnosticCount: bundleMcpThreadConfig.diagnostics.length,
4973
+ nativeToolSurfaceEnabled
4974
+ });
4867
4975
  let yieldDetected = false;
4868
4976
  const tools = await buildDynamicTools({
4869
4977
  params,
@@ -4875,6 +4983,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
4875
4983
  runAbortController,
4876
4984
  sessionAgentId,
4877
4985
  pluginConfig,
4986
+ profilerEnabled,
4878
4987
  onYieldDetected: () => {
4879
4988
  yieldDetected = true;
4880
4989
  }
@@ -4891,6 +5000,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
4891
5000
  runAbortController,
4892
5001
  sessionAgentId,
4893
5002
  pluginConfig,
5003
+ profilerEnabled,
4894
5004
  forceHeartbeatTool: true,
4895
5005
  ignoreRuntimePlan: true,
4896
5006
  onYieldDetected: () => {
@@ -5076,6 +5186,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
5076
5186
  let thread;
5077
5187
  let trajectoryEndRecorded = false;
5078
5188
  let nativeHookRelay;
5189
+ let releaseSharedClientLease;
5079
5190
  let startupClientForCleanup;
5080
5191
  let sandboxExecEnvironmentAcquired = false;
5081
5192
  const releaseSandboxExecEnvironment = async () => {
@@ -5159,106 +5270,120 @@ async function runCodexAppServerAttempt(params, options = {}) {
5159
5270
  operation: async () => {
5160
5271
  let attemptedClient;
5161
5272
  const startupAttempt = async () => {
5162
- const startupClient = await attemptClientFactory(appServer.start, startupAuthProfileId, agentDir, params.config);
5163
- attemptedClient = startupClient;
5164
- startupClientForCleanup = startupClient;
5165
- await ensureCodexComputerUse({
5166
- client: startupClient,
5167
- pluginConfig,
5168
- timeoutMs: appServer.requestTimeoutMs,
5169
- signal: runAbortController.signal
5170
- });
5171
- let startupSandboxEnvironment;
5172
- let startupSandboxEnvironmentAcquired = false;
5173
- const releaseStartupSandboxEnvironment = async () => {
5174
- if (startupSandboxEnvironmentAcquired) {
5175
- startupSandboxEnvironmentAcquired = false;
5176
- await releaseCodexSandboxExecServerEnvironment(sandbox);
5177
- }
5178
- };
5179
- releaseStartupResourcesOnTimeout = releaseStartupSandboxEnvironment;
5273
+ let startupClientLease;
5274
+ let startupAttemptSucceeded = false;
5180
5275
  try {
5181
- startupSandboxEnvironment = shouldRequireCodexSandboxExecServerEnvironment({
5182
- sandbox,
5183
- nativeToolSurfaceEnabled,
5184
- sandboxExecServerEnabled
5185
- }) ? await ensureCodexSandboxExecServerEnvironment({
5276
+ const startupClient = await attemptClientFactory(appServer.start, startupAuthProfileId, agentDir, params.config);
5277
+ startupClientLease = () => {
5278
+ releaseLeasedSharedCodexAppServerClient(startupClient);
5279
+ };
5280
+ releaseSharedClientLease = startupClientLease;
5281
+ attemptedClient = startupClient;
5282
+ startupClientForCleanup = startupClient;
5283
+ await ensureCodexComputerUse({
5186
5284
  client: startupClient,
5187
- sandbox: sandbox ?? null,
5188
- appServerStartOptions: appServer.start,
5285
+ pluginConfig,
5189
5286
  timeoutMs: appServer.requestTimeoutMs,
5190
5287
  signal: runAbortController.signal
5191
- }) : void 0;
5192
- startupSandboxEnvironmentAcquired = Boolean(startupSandboxEnvironment);
5193
- if (runAbortController.signal.aborted) {
5194
- await releaseStartupSandboxEnvironment();
5195
- throw new Error("codex app-server startup aborted");
5196
- }
5197
- if (sandbox?.enabled && nativeToolSurfaceEnabled && sandboxExecServerEnabled && !startupSandboxEnvironment) throw new Error("Codex app-server did not register an OpenClaw sandbox exec-server environment.");
5198
- } catch (error) {
5199
- await releaseStartupSandboxEnvironment();
5200
- throw error;
5201
- }
5202
- const startupEnvironmentSelection = resolveCodexSandboxEnvironmentSelection(startupSandboxEnvironment, nativeToolSurfaceEnabled);
5203
- const startupExecutionCwd = resolveCodexAppServerExecutionCwd({
5204
- effectiveWorkspace,
5205
- environment: startupSandboxEnvironment,
5206
- nativeToolSurfaceEnabled
5207
- });
5208
- const startupSandboxPolicy = startupSandboxEnvironment ? resolveCodexExternalSandboxPolicyForOpenClawSandbox(sandbox) : void 0;
5209
- const buildThreadLifecycleParams = () => ({
5210
- client: startupClient,
5211
- params: buildActiveRunAttemptParams(),
5212
- agentId: sessionAgentId,
5213
- cwd: startupExecutionCwd,
5214
- dynamicTools: toolBridge.specs,
5215
- appServer: pluginAppServer,
5216
- developerInstructions: promptBuild.developerInstructions,
5217
- config: threadConfig,
5218
- finalConfigPatch: nativeHookRelayConfig,
5219
- nativeCodeModeEnabled: nativeToolSurfaceEnabled,
5220
- nativeCodeModeOnlyEnabled: appServer.codeModeOnly,
5221
- userMcpServersEnabled: nativeToolSurfaceEnabled,
5222
- mcpServersFingerprint: bundleMcpThreadConfig.fingerprint,
5223
- mcpServersFingerprintEvaluated: bundleMcpThreadConfig.evaluated,
5224
- environmentSelection: startupEnvironmentSelection,
5225
- contextEngineProjection,
5226
- pluginThreadConfig: pluginThreadConfigRequired ? {
5227
- enabled: true,
5228
- inputFingerprint: pluginThreadConfigInputFingerprint,
5229
- enabledPluginConfigKeys,
5230
- build: () => buildCodexPluginThreadConfig({
5231
- pluginConfig: pluginThreadConfigPluginConfig,
5232
- request: (method, requestParams) => startupClient.request(method, requestParams, {
5233
- timeoutMs: appServer.requestTimeoutMs,
5234
- signal: runAbortController.signal
5235
- }),
5236
- appCache: defaultCodexAppInventoryCache,
5237
- appCacheKey: pluginAppCacheKey
5238
- })
5239
- } : void 0
5240
- });
5241
- try {
5242
- restartContextEngineCodexThread = () => startOrResumeThread(buildThreadLifecycleParams());
5243
- const startupThread = await startOrResumeThread(buildThreadLifecycleParams());
5244
- if (runAbortController.signal.aborted) {
5288
+ });
5289
+ let startupSandboxEnvironment;
5290
+ let startupSandboxEnvironmentAcquired = false;
5291
+ const releaseStartupSandboxEnvironment = async () => {
5292
+ if (startupSandboxEnvironmentAcquired) {
5293
+ startupSandboxEnvironmentAcquired = false;
5294
+ await releaseCodexSandboxExecServerEnvironment(sandbox);
5295
+ }
5296
+ };
5297
+ releaseStartupResourcesOnTimeout = releaseStartupSandboxEnvironment;
5298
+ try {
5299
+ startupSandboxEnvironment = shouldRequireCodexSandboxExecServerEnvironment({
5300
+ sandbox,
5301
+ nativeToolSurfaceEnabled,
5302
+ sandboxExecServerEnabled
5303
+ }) ? await ensureCodexSandboxExecServerEnvironment({
5304
+ client: startupClient,
5305
+ sandbox: sandbox ?? null,
5306
+ appServerStartOptions: appServer.start,
5307
+ timeoutMs: appServer.requestTimeoutMs,
5308
+ signal: runAbortController.signal
5309
+ }) : void 0;
5310
+ startupSandboxEnvironmentAcquired = Boolean(startupSandboxEnvironment);
5311
+ if (runAbortController.signal.aborted) {
5312
+ await releaseStartupSandboxEnvironment();
5313
+ throw new Error("codex app-server startup aborted");
5314
+ }
5315
+ if (sandbox?.enabled && nativeToolSurfaceEnabled && sandboxExecServerEnabled && !startupSandboxEnvironment) throw new Error("Codex app-server did not register an OpenClaw sandbox exec-server environment.");
5316
+ } catch (error) {
5245
5317
  await releaseStartupSandboxEnvironment();
5246
- throw new Error("codex app-server startup aborted");
5318
+ throw error;
5247
5319
  }
5248
- startupSandboxEnvironmentAcquired = false;
5249
- return {
5320
+ const startupEnvironmentSelection = resolveCodexSandboxEnvironmentSelection(startupSandboxEnvironment, nativeToolSurfaceEnabled);
5321
+ const startupExecutionCwd = resolveCodexAppServerExecutionCwd({
5322
+ effectiveWorkspace,
5323
+ environment: startupSandboxEnvironment,
5324
+ nativeToolSurfaceEnabled
5325
+ });
5326
+ const startupSandboxPolicy = startupSandboxEnvironment ? resolveCodexExternalSandboxPolicyForOpenClawSandbox(sandbox) : void 0;
5327
+ const buildThreadLifecycleParams = () => ({
5250
5328
  client: startupClient,
5251
- thread: startupThread,
5252
- sandboxEnvironment: startupSandboxEnvironment,
5329
+ params: buildActiveRunAttemptParams(),
5330
+ agentId: sessionAgentId,
5331
+ cwd: startupExecutionCwd,
5332
+ dynamicTools: toolBridge.specs,
5333
+ appServer: pluginAppServer,
5334
+ developerInstructions: promptBuild.developerInstructions,
5335
+ config: threadConfig,
5336
+ finalConfigPatch: nativeHookRelayConfig,
5337
+ nativeCodeModeEnabled: nativeToolSurfaceEnabled,
5338
+ nativeCodeModeOnlyEnabled: appServer.codeModeOnly,
5339
+ userMcpServersEnabled: nativeToolSurfaceEnabled,
5340
+ mcpServersFingerprint: bundleMcpThreadConfig.fingerprint,
5341
+ mcpServersFingerprintEvaluated: bundleMcpThreadConfig.evaluated,
5253
5342
  environmentSelection: startupEnvironmentSelection,
5254
- executionCwd: startupExecutionCwd,
5255
- sandboxPolicy: startupSandboxPolicy
5256
- };
5257
- } catch (error) {
5258
- await releaseStartupSandboxEnvironment();
5259
- throw error;
5343
+ contextEngineProjection,
5344
+ pluginThreadConfig: pluginThreadConfigRequired ? {
5345
+ enabled: true,
5346
+ inputFingerprint: pluginThreadConfigInputFingerprint,
5347
+ enabledPluginConfigKeys,
5348
+ build: () => buildCodexPluginThreadConfig({
5349
+ pluginConfig: pluginThreadConfigPluginConfig,
5350
+ request: (method, requestParams) => startupClient.request(method, requestParams, {
5351
+ timeoutMs: appServer.requestTimeoutMs,
5352
+ signal: runAbortController.signal
5353
+ }),
5354
+ appCache: defaultCodexAppInventoryCache,
5355
+ appCacheKey: pluginAppCacheKey
5356
+ })
5357
+ } : void 0
5358
+ });
5359
+ try {
5360
+ restartContextEngineCodexThread = () => startOrResumeThread(buildThreadLifecycleParams());
5361
+ const startupThread = await startOrResumeThread(buildThreadLifecycleParams());
5362
+ if (runAbortController.signal.aborted) {
5363
+ await releaseStartupSandboxEnvironment();
5364
+ throw new Error("codex app-server startup aborted");
5365
+ }
5366
+ startupSandboxEnvironmentAcquired = false;
5367
+ startupAttemptSucceeded = true;
5368
+ return {
5369
+ client: startupClient,
5370
+ thread: startupThread,
5371
+ sandboxEnvironment: startupSandboxEnvironment,
5372
+ environmentSelection: startupEnvironmentSelection,
5373
+ executionCwd: startupExecutionCwd,
5374
+ sandboxPolicy: startupSandboxPolicy
5375
+ };
5376
+ } catch (error) {
5377
+ await releaseStartupSandboxEnvironment();
5378
+ throw error;
5379
+ } finally {
5380
+ if (releaseStartupResourcesOnTimeout === releaseStartupSandboxEnvironment) releaseStartupResourcesOnTimeout = void 0;
5381
+ }
5260
5382
  } finally {
5261
- if (releaseStartupResourcesOnTimeout === releaseStartupSandboxEnvironment) releaseStartupResourcesOnTimeout = void 0;
5383
+ if (!startupAttemptSucceeded) {
5384
+ if (releaseSharedClientLease === startupClientLease) releaseSharedClientLease = void 0;
5385
+ startupClientLease?.();
5386
+ }
5262
5387
  }
5263
5388
  };
5264
5389
  for (let attempt = 1; attempt <= CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS; attempt += 1) try {
@@ -5465,7 +5590,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
5465
5590
  runAbortController.abort("turn_progress_idle_timeout");
5466
5591
  };
5467
5592
  const fireTurnCompletionIdleTimeout = () => {
5468
- if (completed || runAbortController.signal.aborted || !turnCompletionIdleWatchArmed || activeAppServerTurnRequests > 0) return;
5593
+ if (completed || terminalTurnNotificationQueued || runAbortController.signal.aborted || !turnCompletionIdleWatchArmed || activeAppServerTurnRequests > 0) return;
5469
5594
  const timeoutMs = turnCompletionIdleTimeoutOverrideMs ?? turnCompletionIdleTimeoutMs;
5470
5595
  const idleMs = Math.max(0, Date.now() - turnCompletionLastActivityAt);
5471
5596
  if (idleMs < timeoutMs) {
@@ -5495,7 +5620,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
5495
5620
  runAbortController.abort("turn_completion_idle_timeout");
5496
5621
  };
5497
5622
  const fireTurnTerminalIdleTimeout = () => {
5498
- if (completed || runAbortController.signal.aborted || !turnTerminalIdleWatchArmed || activeAppServerTurnRequests > 0) return;
5623
+ if (completed || terminalTurnNotificationQueued || runAbortController.signal.aborted || !turnTerminalIdleWatchArmed || activeAppServerTurnRequests > 0) return;
5499
5624
  const idleMs = Math.max(0, Date.now() - turnCompletionLastActivityAt);
5500
5625
  if (idleMs < turnTerminalIdleTimeoutMs) {
5501
5626
  scheduleTurnTerminalIdleWatch();
@@ -5817,14 +5942,23 @@ async function runCodexAppServerAttempt(params, options = {}) {
5817
5942
  threadId: thread.threadId,
5818
5943
  ...turnId ? { turnId } : {}
5819
5944
  });
5820
- embeddedAgentLog.debug("codex app-server raw notification received", correlation);
5821
- if (notification.method === "turn/completed" && correlation.matchesActiveTurn === false) embeddedAgentLog.warn("codex app-server turn/completed did not match active turn", correlation);
5945
+ embeddedAgentLog.trace("codex app-server raw notification received", correlation);
5946
+ if (notification.method === "turn/completed" && correlation.matchesActiveTurn === false) if (correlation.matchesActiveThread) embeddedAgentLog.warn("codex app-server turn/completed did not match active turn", correlation);
5947
+ else embeddedAgentLog.debug("codex app-server turn/completed ignored for other subscribed thread", correlation);
5948
+ if (isCodexNotificationOutsideActiveRun(correlation)) return Promise.resolve();
5822
5949
  if (!projector || !turnId) {
5823
5950
  userInputBridge?.handleNotification(notification);
5824
5951
  pendingNotifications.push(notification);
5825
5952
  return Promise.resolve();
5826
5953
  }
5827
5954
  if (isTerminalTurnNotificationForTurn(notification, turnId)) terminalTurnNotificationQueued = true;
5955
+ if (correlation.matchesActiveTurn !== false) {
5956
+ const now = Date.now();
5957
+ turnCompletionLastActivityAt = now;
5958
+ turnCompletionLastActivityReason = `notification:${notification.method}`;
5959
+ turnAttemptLastProgressAt = now;
5960
+ turnAttemptLastProgressReason = `notification:${notification.method}`;
5961
+ }
5828
5962
  notificationQueue = notificationQueue.then(() => handleNotification(notification), () => handleNotification(notification));
5829
5963
  return notificationQueue;
5830
5964
  };
@@ -6276,6 +6410,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
6276
6410
  }
6277
6411
  });
6278
6412
  params.abortSignal?.removeEventListener("abort", abortFromUpstream);
6413
+ releaseSharedClientLease?.();
6414
+ releaseSharedClientLease = void 0;
6279
6415
  if (usageLimitError) {
6280
6416
  await markCodexAuthProfileBlockedFromRateLimits({
6281
6417
  params,
@@ -6292,7 +6428,11 @@ async function runCodexAppServerAttempt(params, options = {}) {
6292
6428
  throw turnStartError;
6293
6429
  }
6294
6430
  }
6295
- if (!turn) throw new Error("codex app-server turn/start failed without an error");
6431
+ if (!turn) {
6432
+ releaseSharedClientLease?.();
6433
+ releaseSharedClientLease = void 0;
6434
+ throw new Error("codex app-server turn/start failed without an error");
6435
+ }
6296
6436
  turnId = turn.turn.id;
6297
6437
  const activeTurnId = turn.turn.id;
6298
6438
  emitExecutionPhaseOnce("turn_accepted", { phase: "turn_accepted" });
@@ -6364,9 +6504,11 @@ async function runCodexAppServerAttempt(params, options = {}) {
6364
6504
  abort: () => runAbortController.abort("aborted")
6365
6505
  };
6366
6506
  setActiveEmbeddedRun(params.sessionId, handle, params.sessionKey);
6507
+ const notifyUserMessagePersisted = createCodexAppServerUserMessagePersistenceNotifier(params);
6367
6508
  mirrorPromptAtTurnStartBestEffort({
6368
6509
  params,
6369
6510
  agentId: sessionAgentId,
6511
+ notifyUserMessagePersisted,
6370
6512
  sessionKey: sandboxSessionKey,
6371
6513
  threadId: thread.threadId,
6372
6514
  turnId: activeTurnId
@@ -6375,16 +6517,19 @@ async function runCodexAppServerAttempt(params, options = {}) {
6375
6517
  turnTerminalIdleWatchArmed = true;
6376
6518
  touchTurnCompletionActivity("turn:start", { attemptProgress: true });
6377
6519
  const abortListener = () => {
6378
- const shouldRetireClient = timedOut;
6520
+ if (timedOut) {
6521
+ retireCodexAppServerClientAfterTimedOutTurn(client, {
6522
+ threadId: thread.threadId,
6523
+ turnId: activeTurnId,
6524
+ reason: String(runAbortController.signal.reason ?? "timeout")
6525
+ }).finally(() => {
6526
+ resolveCompletion?.();
6527
+ });
6528
+ return;
6529
+ }
6379
6530
  interruptCodexTurnBestEffort(client, {
6380
6531
  threadId: thread.threadId,
6381
- turnId: activeTurnId,
6382
- timeoutMs: shouldRetireClient ? CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS : void 0
6383
- });
6384
- if (shouldRetireClient) retireCodexAppServerClientAfterTimedOutTurn(client, {
6385
- threadId: thread.threadId,
6386
- turnId: activeTurnId,
6387
- reason: String(runAbortController.signal.reason ?? "timeout")
6532
+ turnId: activeTurnId
6388
6533
  });
6389
6534
  resolveCompletion?.();
6390
6535
  };
@@ -6443,6 +6588,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
6443
6588
  await mirrorTranscriptBestEffort({
6444
6589
  params,
6445
6590
  agentId: sessionAgentId,
6591
+ notifyUserMessagePersisted,
6446
6592
  result,
6447
6593
  sessionKey: contextSessionKey,
6448
6594
  threadId: thread.threadId,
@@ -6567,6 +6713,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
6567
6713
  notificationCleanup();
6568
6714
  requestCleanup();
6569
6715
  closeCleanup?.();
6716
+ releaseSharedClientLease?.();
6570
6717
  if (nativeHookRelay) if (shouldDelayNativeHookRelayUnregister) scheduleCodexNativeHookRelayUnregister({
6571
6718
  relay: nativeHookRelay,
6572
6719
  hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec
@@ -6799,7 +6946,10 @@ function createCodexNativeHookRelay(params) {
6799
6946
  turnStartTimeoutMs: params.turnStartTimeoutMs
6800
6947
  }),
6801
6948
  signal: params.signal,
6802
- command: { timeoutMs: params.options?.gatewayTimeoutMs }
6949
+ command: {
6950
+ nice: 10,
6951
+ timeoutMs: params.options?.gatewayTimeoutMs
6952
+ }
6803
6953
  });
6804
6954
  }
6805
6955
  function resolveCodexNativeHookRelayEvents(params) {
@@ -6867,17 +7017,39 @@ async function unsubscribeCodexThreadBestEffort(client, params) {
6867
7017
  });
6868
7018
  }
6869
7019
  }
6870
- function retireCodexAppServerClientAfterTimedOutTurn(client, params) {
6871
- const clearedSharedClient = clearSharedCodexAppServerClientIfCurrent(client);
6872
- if (!clearedSharedClient) {
7020
+ async function retireCodexAppServerClientAfterTimedOutTurn(client, params) {
7021
+ const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client);
7022
+ const detachedSharedClient = Boolean(retiredSharedClient);
7023
+ interruptCodexTurnBestEffort(client, {
7024
+ threadId: params.threadId,
7025
+ turnId: params.turnId,
7026
+ timeoutMs: CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS
7027
+ });
7028
+ await unsubscribeCodexThreadBestEffort(client, {
7029
+ threadId: params.threadId,
7030
+ timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
7031
+ });
7032
+ let closedClient = retiredSharedClient?.closed ?? false;
7033
+ if (!detachedSharedClient) {
6873
7034
  const close = client.close;
6874
- if (typeof close === "function") close.call(client);
7035
+ if (typeof close === "function") try {
7036
+ close.call(client);
7037
+ closedClient = true;
7038
+ } catch (error) {
7039
+ embeddedAgentLog.debug("codex app-server client close failed during timeout cleanup", {
7040
+ threadId: params.threadId,
7041
+ turnId: params.turnId,
7042
+ error
7043
+ });
7044
+ }
6875
7045
  }
6876
7046
  embeddedAgentLog.warn("codex app-server client retired after timed-out turn", {
6877
7047
  threadId: params.threadId,
6878
7048
  turnId: params.turnId,
6879
7049
  reason: params.reason,
6880
- clearedSharedClient
7050
+ detachedSharedClient,
7051
+ closedClient,
7052
+ activeSharedClientLeases: retiredSharedClient?.activeLeases ?? 0
6881
7053
  });
6882
7054
  }
6883
7055
  function resolveOpenClawCodingToolsSessionKeys(params, sandboxSessionKey) {
@@ -6895,12 +7067,54 @@ function resolveCodexAppServerHookChannelId(params, sandboxSessionKey) {
6895
7067
  messageTo: params.messageTo
6896
7068
  }).channelId;
6897
7069
  }
7070
+ const CODEX_DYNAMIC_TOOL_BUILD_WARN_TOTAL_MS = 1e3;
7071
+ const CODEX_DYNAMIC_TOOL_BUILD_WARN_STAGE_MS = 500;
7072
+ function createCodexDynamicToolBuildStageTracker(options = {}) {
7073
+ if (!options.enabled) return {
7074
+ mark() {},
7075
+ snapshot() {
7076
+ return {
7077
+ totalMs: 0,
7078
+ stages: []
7079
+ };
7080
+ }
7081
+ };
7082
+ const startedAt = Date.now();
7083
+ let previousAt = startedAt;
7084
+ const stages = [];
7085
+ const toMs = (value) => Math.max(0, Math.round(value));
7086
+ return {
7087
+ mark(name) {
7088
+ const currentAt = Date.now();
7089
+ stages.push({
7090
+ name,
7091
+ durationMs: toMs(currentAt - previousAt),
7092
+ elapsedMs: toMs(currentAt - startedAt)
7093
+ });
7094
+ previousAt = currentAt;
7095
+ },
7096
+ snapshot() {
7097
+ return {
7098
+ totalMs: toMs(Date.now() - startedAt),
7099
+ stages: stages.slice()
7100
+ };
7101
+ }
7102
+ };
7103
+ }
7104
+ function shouldWarnCodexDynamicToolBuildStageSummary(summary) {
7105
+ return summary.totalMs >= CODEX_DYNAMIC_TOOL_BUILD_WARN_TOTAL_MS || summary.stages.some((stage) => stage.durationMs >= CODEX_DYNAMIC_TOOL_BUILD_WARN_STAGE_MS);
7106
+ }
7107
+ function formatCodexDynamicToolBuildStageSummary(summary) {
7108
+ return summary.stages.length > 0 ? summary.stages.map((stage) => `${stage.name}:${stage.durationMs}ms@${stage.elapsedMs}ms`).join(",") : "none";
7109
+ }
6898
7110
  async function buildDynamicTools(input) {
6899
7111
  const { params } = input;
6900
7112
  if (params.disableTools || !supportsModelTools(params.model)) return [];
7113
+ const toolBuildStages = createCodexDynamicToolBuildStageTracker({ enabled: input.profilerEnabled });
6901
7114
  const modelHasVision = params.model.input?.includes("image") ?? false;
6902
7115
  const agentDir = params.agentDir ?? resolveAgentDir$1(params.config ?? {}, input.sessionAgentId);
6903
7116
  const createOpenClawCodingTools = (await import("openclaw/plugin-sdk/agent-harness")).createOpenClawCodingTools;
7117
+ toolBuildStages.mark("load-agent-harness-tools");
6904
7118
  const sessionKeys = resolveOpenClawCodingToolsSessionKeys(params, input.sandboxSessionKey);
6905
7119
  const allTools = createOpenClawCodingTools({
6906
7120
  agentId: input.sessionAgentId,
@@ -6941,7 +7155,7 @@ async function buildDynamicTools(input) {
6941
7155
  modelCompat: params.model.compat && typeof params.model.compat === "object" ? params.model.compat : void 0,
6942
7156
  modelApi: params.model.api,
6943
7157
  modelContextWindowTokens: params.model.contextWindow,
6944
- modelAuthMode: resolveModelAuthMode(params.model.provider, params.config, void 0, { workspaceDir: input.effectiveWorkspace }),
7158
+ modelAuthMode: resolveModelAuthMode(params.model.provider, params.config, params.toolAuthProfileStore ?? params.authProfileStore, { workspaceDir: input.effectiveWorkspace }),
6945
7159
  suppressManagedWebSearch: false,
6946
7160
  currentChannelId: params.currentChannelId,
6947
7161
  hookChannelId: resolveCodexAppServerHookChannelId(params, input.sandboxSessionKey),
@@ -6965,13 +7179,22 @@ async function buildDynamicTools(input) {
6965
7179
  message
6966
7180
  }
6967
7181
  });
7182
+ },
7183
+ recordToolPrepStage: (name) => {
7184
+ toolBuildStages.mark(name);
6968
7185
  }
6969
7186
  });
6970
- const filteredTools = filterCodexDynamicToolsForAllowlist(filterToolsForVisionInputs(addNodeShellDynamicToolsIfNeeded(addSandboxShellDynamicToolsIfAvailable(isCodexMemoryFlushRun(params) ? filterCodexMemoryFlushDynamicTools(allTools) : filterCodexDynamicTools(allTools, input.pluginConfig), allTools, input), allTools, input), {
7187
+ toolBuildStages.mark("create-openclaw-coding-tools");
7188
+ const codexFilteredTools = addNodeShellDynamicToolsIfNeeded(addSandboxShellDynamicToolsIfAvailable(isCodexMemoryFlushRun(params) ? filterCodexMemoryFlushDynamicTools(allTools) : filterCodexDynamicTools(allTools, input.pluginConfig), allTools, input), allTools, input);
7189
+ toolBuildStages.mark("codex-filtering");
7190
+ const visionFilteredTools = filterToolsForVisionInputs(codexFilteredTools, {
6971
7191
  modelHasVision,
6972
7192
  hasInboundImages: (params.images?.length ?? 0) > 0
6973
- }), includeForcedCodexDynamicToolAllow(params.toolsAllow, params));
6974
- return normalizeAgentRuntimeTools({
7193
+ });
7194
+ toolBuildStages.mark("vision-filtering");
7195
+ const filteredTools = filterCodexDynamicToolsForAllowlist(visionFilteredTools, includeForcedCodexDynamicToolAllow(params.toolsAllow, params));
7196
+ toolBuildStages.mark("allowlist-filter");
7197
+ const normalizedTools = normalizeAgentRuntimeTools({
6975
7198
  runtimePlan: input.ignoreRuntimePlan ? void 0 : params.runtimePlan,
6976
7199
  tools: filteredTools,
6977
7200
  provider: params.provider,
@@ -6982,6 +7205,27 @@ async function buildDynamicTools(input) {
6982
7205
  modelApi: params.model.api,
6983
7206
  model: params.model
6984
7207
  });
7208
+ toolBuildStages.mark("runtime-normalization");
7209
+ const summary = toolBuildStages.snapshot();
7210
+ if (shouldWarnCodexDynamicToolBuildStageSummary(summary)) {
7211
+ const phase = input.forceHeartbeatTool ? "registered-tools" : "runtime-tools";
7212
+ embeddedAgentLog.warn(`codex app-server dynamic tool build timings runId=${params.runId} sessionId=${params.sessionId} phase=${phase} totalMs=${summary.totalMs} stages=${formatCodexDynamicToolBuildStageSummary(summary)}`, {
7213
+ runId: params.runId,
7214
+ sessionId: params.sessionId,
7215
+ phase,
7216
+ totalMs: summary.totalMs,
7217
+ stages: summary.stages,
7218
+ allToolCount: allTools.length,
7219
+ codexFilteredToolCount: codexFilteredTools.length,
7220
+ visionFilteredToolCount: visionFilteredTools.length,
7221
+ filteredToolCount: filteredTools.length,
7222
+ normalizedToolCount: normalizedTools.length,
7223
+ forceHeartbeatTool: input.forceHeartbeatTool === true,
7224
+ ignoreRuntimePlan: input.ignoreRuntimePlan === true,
7225
+ nativeToolSurfaceEnabled: input.nativeToolSurfaceEnabled === true
7226
+ });
7227
+ }
7228
+ return normalizedTools;
6985
7229
  }
6986
7230
  function includeForcedCodexDynamicToolAllow(toolsAllow, params) {
6987
7231
  if (toolsAllow === void 0 || hasWildcardCodexToolsAllow(toolsAllow)) return toolsAllow;
@@ -7151,11 +7395,33 @@ function shouldProjectMirroredHistoryForCodexStart(params) {
7151
7395
  if (!params.historyMessages.some((message) => message.role === "user")) return false;
7152
7396
  if (params.forceProject) return true;
7153
7397
  if (!params.startupBinding?.threadId) return true;
7398
+ if (hasUserVisibleHistoryAfterCodexBinding({
7399
+ startupBinding: params.startupBinding,
7400
+ historyMessages: params.historyMessages
7401
+ })) return true;
7154
7402
  return !areCodexDynamicToolFingerprintsCompatible({
7155
7403
  previous: params.startupBinding.dynamicToolsFingerprint,
7156
7404
  next: params.dynamicToolsFingerprint
7157
7405
  });
7158
7406
  }
7407
+ function hasUserVisibleHistoryAfterCodexBinding(params) {
7408
+ const bindingUpdatedAt = Date.parse(params.startupBinding.updatedAt);
7409
+ if (!Number.isFinite(bindingUpdatedAt)) return false;
7410
+ return params.historyMessages.some((message) => {
7411
+ if (message.role !== "user" && message.role !== "assistant") return false;
7412
+ if (isCodexAppServerMirroredTranscriptMessage(message)) return false;
7413
+ const timestamp = typeof message.timestamp === "number" ? message.timestamp : typeof message.timestamp === "string" ? Date.parse(message.timestamp) : NaN;
7414
+ return Number.isFinite(timestamp) && timestamp > bindingUpdatedAt;
7415
+ });
7416
+ }
7417
+ function isCodexAppServerMirroredTranscriptMessage(message) {
7418
+ const record = message;
7419
+ const idempotencyKey = record.idempotencyKey;
7420
+ if (typeof idempotencyKey === "string" && idempotencyKey.startsWith("codex-app-server:")) return true;
7421
+ const meta = record["__openclaw"];
7422
+ if (!meta || typeof meta !== "object" || Array.isArray(meta)) return false;
7423
+ return typeof meta.mirrorIdentity === "string";
7424
+ }
7159
7425
  function readContextEngineThreadBootstrapProjection(projection) {
7160
7426
  if (projection?.mode !== "thread_bootstrap") return;
7161
7427
  const epoch = projection.epoch?.trim();
@@ -7473,6 +7739,11 @@ function readRawAssistantTextPreview(item) {
7473
7739
  function isTurnNotification(value, threadId, turnId) {
7474
7740
  return isCodexNotificationForTurn(value, threadId, turnId);
7475
7741
  }
7742
+ function isCodexNotificationOutsideActiveRun(correlation) {
7743
+ if (!Boolean(correlation.threadId || correlation.nestedTurnThreadId)) return false;
7744
+ if (!correlation.matchesActiveThread) return true;
7745
+ return Boolean(correlation.turnId || correlation.nestedTurnId) && correlation.matchesActiveTurn === false;
7746
+ }
7476
7747
  function isCurrentThreadTurnRequestParams(value, threadId, turnId) {
7477
7748
  if (!isJsonObject(value)) return false;
7478
7749
  return readString(value, "threadId") === threadId && readString(value, "turnId") === turnId;
@@ -7742,6 +8013,16 @@ function readPositiveNumber(value) {
7742
8013
  function readNonEmptyString(value) {
7743
8014
  return typeof value === "string" && value.trim().length > 0 ? value : void 0;
7744
8015
  }
8016
+ const CODEX_DELIVERY_HINT_LINES = ["Delivery: to send a message, use the `message` tool.", "Delivery: Final assistant text is not automatically delivered in this run. Use the `message` tool to send user-visible output."];
8017
+ function splitLeadingCodexDeliveryHint(prompt) {
8018
+ const trimmedStart = prompt.trimStart();
8019
+ const matchedHint = CODEX_DELIVERY_HINT_LINES.find((hint) => trimmedStart.startsWith(hint));
8020
+ if (!matchedHint) return { prompt };
8021
+ return {
8022
+ deliveryHint: matchedHint,
8023
+ prompt: trimmedStart.slice(matchedHint.length).replace(/^\s*\n/, "").trimStart()
8024
+ };
8025
+ }
7745
8026
  function buildCodexOpenClawPromptContext(params) {
7746
8027
  if (!shouldInjectCodexOpenClawPromptContext(params.params)) return;
7747
8028
  const sections = [params.skillsPrompt?.trim() ? [
@@ -7766,12 +8047,18 @@ function shouldInjectCodexOpenClawPromptContext(params) {
7766
8047
  }
7767
8048
  function prependCodexOpenClawPromptContext(prompt, context) {
7768
8049
  if (!context?.trim()) return prompt;
7769
- const promptSection = prompt.startsWith("OpenClaw assembled context for this turn:") ? prompt : ["Current user request:", prompt].join("\n");
8050
+ const { deliveryHint, prompt: promptWithoutDeliveryHint } = splitLeadingCodexDeliveryHint(prompt);
8051
+ const promptSection = promptWithoutDeliveryHint.startsWith("OpenClaw assembled context for this turn:") ? promptWithoutDeliveryHint : ["Current user request:", promptWithoutDeliveryHint].join("\n");
8052
+ const deliverySection = deliveryHint ? [
8053
+ "OpenClaw delivery metadata:",
8054
+ "This delivery metadata is runtime routing guidance, not the user's request.",
8055
+ deliveryHint
8056
+ ].join("\n") : void 0;
7770
8057
  return [
7771
8058
  context.trim(),
7772
- "",
8059
+ deliverySection,
7773
8060
  promptSection
7774
- ].join("\n");
8061
+ ].filter(Boolean).join("\n\n");
7775
8062
  }
7776
8063
  function renderCodexWorkspaceBootstrapPromptContext(contextFiles) {
7777
8064
  const files = selectCodexWorkspacePromptContextFiles(contextFiles);
@@ -7882,29 +8169,63 @@ function getCodexContextFileBasename(filePath) {
7882
8169
  }
7883
8170
  async function mirrorTranscriptBestEffort(params) {
7884
8171
  try {
7885
- await mirrorCodexAppServerTranscript({
8172
+ const messages = await resolveFinalCodexMirrorMessages({
8173
+ params: params.params,
8174
+ messagesSnapshot: params.result.messagesSnapshot,
8175
+ turnId: params.turnId
8176
+ });
8177
+ const mirrorResult = await mirrorCodexAppServerTranscript({
7886
8178
  sessionFile: params.params.sessionFile,
7887
8179
  agentId: params.agentId,
7888
8180
  sessionKey: params.sessionKey,
7889
- messages: params.result.messagesSnapshot,
8181
+ messages,
7890
8182
  idempotencyScope: `codex-app-server:${params.threadId}`,
7891
8183
  config: params.params.config
7892
8184
  });
8185
+ for (const message of mirrorResult.userMessagesPresent) params.notifyUserMessagePersisted(message);
7893
8186
  } catch (error) {
7894
8187
  embeddedAgentLog.warn("failed to mirror codex app-server transcript", { error });
7895
8188
  }
7896
8189
  }
8190
+ async function resolveFinalCodexMirrorMessages(params) {
8191
+ if (params.params.suppressNextUserMessagePersistence || !params.params.userTurnTranscriptRecorder) return params.messagesSnapshot;
8192
+ const resolvedPrompt = attachCodexMirrorIdentity(await buildResolvedCodexUserPromptMessage(params.params), `${params.turnId}:prompt`);
8193
+ const firstUserIndex = params.messagesSnapshot.findIndex((message) => message.role === "user");
8194
+ if (firstUserIndex === -1) return [resolvedPrompt, ...params.messagesSnapshot];
8195
+ const messages = params.messagesSnapshot.slice();
8196
+ messages[firstUserIndex] = resolvedPrompt;
8197
+ return messages;
8198
+ }
8199
+ function createCodexAppServerUserMessagePersistenceNotifier(runParams) {
8200
+ let notified = false;
8201
+ return (message) => {
8202
+ if (notified) return;
8203
+ notified = true;
8204
+ runParams.userTurnTranscriptRecorder?.markRuntimePersisted(message);
8205
+ try {
8206
+ runParams.onUserMessagePersisted?.(message);
8207
+ } catch (error) {
8208
+ embeddedAgentLog.warn("codex app-server user persistence notification failed", { error: formatErrorMessage(error) });
8209
+ }
8210
+ };
8211
+ }
7897
8212
  async function mirrorPromptAtTurnStartBestEffort(params) {
7898
8213
  if (params.params.suppressNextUserMessagePersistence) return;
7899
8214
  try {
7900
- await mirrorCodexAppServerTranscript({
7901
- sessionFile: params.params.sessionFile,
7902
- agentId: params.agentId,
7903
- sessionKey: params.sessionKey,
7904
- messages: [attachCodexMirrorIdentity(buildCodexUserPromptMessage(params.params), `${params.turnId}:prompt`)],
7905
- idempotencyScope: `codex-app-server:${params.threadId}`,
7906
- config: params.params.config
7907
- });
8215
+ const mirrorPromise = (async () => {
8216
+ const userPromptMessage = attachCodexMirrorIdentity(await buildResolvedCodexUserPromptMessage(params.params), `${params.turnId}:prompt`);
8217
+ const mirrorResult = await mirrorCodexAppServerTranscript({
8218
+ sessionFile: params.params.sessionFile,
8219
+ agentId: params.agentId,
8220
+ sessionKey: params.sessionKey,
8221
+ messages: [userPromptMessage],
8222
+ idempotencyScope: `codex-app-server:${params.threadId}`,
8223
+ config: params.params.config
8224
+ });
8225
+ for (const message of mirrorResult.userMessagesPresent) params.notifyUserMessagePersisted(message);
8226
+ })();
8227
+ params.params.userTurnTranscriptRecorder?.markRuntimePersistencePending(mirrorPromise);
8228
+ await mirrorPromise;
7908
8229
  } catch (error) {
7909
8230
  embeddedAgentLog.warn("failed to mirror codex app-server prompt at turn start", { error });
7910
8231
  }