@openclaw/codex 2026.8.1-beta.3 → 2026.8.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 (69) hide show
  1. package/dist/api.js +5 -6
  2. package/dist/{attempt-client-cleanup-URjUlCHg.js → attempt-client-cleanup-CpVfwctT.js} +47 -5
  3. package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
  4. package/dist/{binding-connection-DAvZwp96.js → binding-connection-CF7BmrDk.js} +2 -2
  5. package/dist/{bounded-turn-4BjrZEwW.js → bounded-turn-DRC3hcjm.js} +16 -20
  6. package/dist/{capabilities-CH3K57v4.js → capabilities-D6m_TdVy.js} +1 -1
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-D0nZ3qtd.js → command-formatters--UToCokS.js} +4 -3
  9. package/dist/{command-handlers-DC86lgT4.js → command-handlers-CLFx2Ypa.js} +114 -79
  10. package/dist/{compact-COqgBRny.js → compact-5xqW1oCh.js} +13 -12
  11. package/dist/{config-CpNeo5AE.js → config-CcYL77O6.js} +350 -288
  12. package/dist/{conversation-binding-data-CW2GuupV.js → conversation-binding-data-BiodpIzq.js} +2 -1
  13. package/dist/doctor-contract-api.js +121 -16
  14. package/dist/{dynamic-tools-fJZhl4CE.js → dynamic-tools-BmeXjwNZ.js} +243 -1002
  15. package/dist/{effective-mcp-catalog-4YKn51aG.js → effective-mcp-catalog-XXQKgK1U.js} +2 -2
  16. package/dist/{event-projector-assistant-message-BwxV4d6N.js → event-projector-assistant-message-C5-Ds8mA.js} +18 -1
  17. package/dist/{event-projector-usage-CLwNEJqW.js → event-projector-usage-B2r5vhWH.js} +9 -14
  18. package/dist/{harness-DoKQcF8n.js → harness-BgeB7LnD.js} +42 -52
  19. package/dist/harness.js +1 -1
  20. package/dist/index.js +308 -306
  21. package/dist/{isolated-completion-DPMtlH9r.js → isolated-completion-CxTus3vZ.js} +4 -4
  22. package/dist/launch-args-sXOAg0SX.js +84 -0
  23. package/dist/{managed-binary-By4FZC6i.js → managed-binary-ZkGodW19.js} +43 -7
  24. package/dist/media-understanding-provider.js +1 -1
  25. package/dist/model-catalog-DULBcniB.js +101 -0
  26. package/dist/{models-CZw1wClT.js → models-DaWfETQk.js} +27 -29
  27. package/dist/{native-subagent-monitor-GGq3RnOo.js → native-subagent-monitor-vXYUZH37.js} +226 -129
  28. package/dist/{node-exec-server.runtime-Bsu1LdFZ.js → node-exec-server.runtime-DirKVZ5h.js} +9 -6
  29. package/dist/{plugin-approval-roundtrip-C75Yiiwc.js → plugin-approval-roundtrip-CCWQGlAI.js} +2 -1
  30. package/dist/{plugin-marketplace-discovery-CSWDkMrP.js → plugin-marketplace-discovery-BXjIFEiw.js} +38 -46
  31. package/dist/{plugin-metadata-cache-BXH5NF-4.js → plugin-metadata-cache-DQQm4gWT.js} +163 -136
  32. package/dist/{protocol-validators-VAVyEnDp.js → protocol-validators-Bg5rpNxk.js} +148 -5
  33. package/dist/{rate-limits-BI1dMHDG.js → rate-limits-BqHEHXQJ.js} +3 -2
  34. package/dist/{sandbox-guard-C1qEeZ5C.js → request-BWxweEdK.js} +183 -2
  35. package/dist/{run-attempt-BV3HzuCf.js → run-attempt-BVLE4Gbi.js} +706 -517
  36. package/dist/{runtime-artifact-BvyT1uyd.js → runtime-artifact-D9pYp4zL.js} +3 -3
  37. package/dist/{session-binding-BTrLf9Qm.js → session-binding-D0zinCP1.js} +195 -122
  38. package/dist/{session-catalog-ezP_UcRM.js → session-catalog-eMhVm0Ql.js} +448 -313
  39. package/dist/session-catalog-home-id-Dt2GiG5o.js +112 -0
  40. package/dist/{session-cli-B1hKvfkI.js → session-cli-B2lyjJA0.js} +1 -1
  41. package/dist/session-history-C5J1AKzG.js +87 -0
  42. package/dist/session-permission-policy-ScnAyG90.js +160 -0
  43. package/dist/session-retirement-CtUtUTr9.js +90 -0
  44. package/dist/{session-upstream-marker-D_2npHlA.js → session-upstream-marker-gqrYIRHk.js} +2 -3
  45. package/dist/settled-turn-context-Bk3IUwod.js +290 -0
  46. package/dist/settled-turn-finalizer-BXLN9PfJ.js +104 -0
  47. package/dist/{shared-client-Dw-rzii0.js → shared-client-B1tT5U6r.js} +1738 -806
  48. package/dist/{side-question-DbXkJZzg.js → side-question-CZ-mCw-t.js} +76 -43
  49. package/dist/{thread-lifecycle-DKdrnTMX.js → thread-lifecycle-CGGKP720.js} +991 -455
  50. package/dist/{thread-ownership-i2NBwDUL.js → thread-ownership-RkaoZCmQ.js} +18 -8
  51. package/dist/{plugin-app-cache-key-DMNQ8Zi8.js → thread-resume-BFt1-ZDa.js} +55 -8
  52. package/dist/{transcript-mirror-BU0_LKAL.js → transcript-mirror-1ep4XjzF.js} +124 -18
  53. package/dist/transport-stdio-NT_Dpd9V.js +724 -0
  54. package/dist/{upstream-prompt-provenance-CFmsKwr9.js → upstream-prompt-provenance-LphB8slG.js} +8 -4
  55. package/dist/{upstream-session-fork-BDVJCLAD.js → upstream-session-fork-Dc8BFExw.js} +76 -90
  56. package/dist/{usage-9dKJCgcy.js → usage-DUhd58gI.js} +3 -3
  57. package/dist/{web-search-provider.runtime-DCdx4DGu.js → web-search-provider.runtime-BEmlE7ov.js} +3 -3
  58. package/openclaw.plugin.json +19 -1
  59. package/package.json +8 -8
  60. package/dist/app-server-policy-bg5QPSVP.js +0 -25
  61. package/dist/command-rpc-xUIIonsO.js +0 -90
  62. package/dist/incognito-session-ET_ufDH-.js +0 -2
  63. package/dist/model-catalog-CmGoqVKB.js +0 -48
  64. package/dist/request-DSK0W6YI.js +0 -157
  65. package/dist/session-catalog-home-id-BNwAJ9D7.js +0 -18
  66. package/dist/session-retirement-CE4iMiLh.js +0 -36
  67. package/dist/settled-turn-finalizer-Dx1onIqg.js +0 -328
  68. package/dist/thread-resume-CdJk2Ehf.js +0 -30
  69. package/dist/transport-stdio-DhoGy33Z.js +0 -99
@@ -1,33 +1,38 @@
1
- import { _ as CODEX_PLUGINS_MARKETPLACE_NAME, g as resolveCodexPluginsPolicy, n as codexSandboxPolicyForTurn, v as CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME } from "./config-CpNeo5AE.js";
2
- import { B as CodexAppServerRpcError, M as isCodexAppServerConnectionClosedError, R as resolveCodexAppServerClientInstanceId, U as consumeCodexAppServerLiveThread, d as retainSharedCodexAppServerClientByInstanceId, k as getCodexAppServerClientInstanceId, lt as flattenCodexDynamicToolFunctions, pt as resolveCodexAppServerLocalHomeDir, s as isCodexAppServerStartSelectionChangedError, ut as isJsonObject } from "./shared-client-Dw-rzii0.js";
3
- import { a as hashCodexAppServerBindingFingerprint, c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, i as createCodexSessionGenerationSupersededError, n as assertCodexBindingMayBeReplaced, o as isCodexAppServerNativeAuthProfile, s as normalizeCodexAppServerBindingModelProvider } from "./session-binding-BTrLf9Qm.js";
4
- import { a as closeCodexStartupClientBestEffort, c as isCodexAppServerUnsafeSubscriptionError, d as unsubscribeCodexThreadBestEffort, n as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, r as CodexAppServerUnsafeSubscriptionError } from "./attempt-client-cleanup-URjUlCHg.js";
5
- import { a as assertCodexThreadStartResponse, r as assertCodexThreadForkResponse } from "./protocol-validators-VAVyEnDp.js";
6
- import { o as defaultCodexAppInventoryCache, s as serializeCodexAppInventoryError, t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-DMNQ8Zi8.js";
7
- import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
8
- import { n as codexCatalogHomeId } from "./session-catalog-home-id-BNwAJ9D7.js";
9
- import { t as resumeCodexAppServerThread } from "./thread-resume-CdJk2Ehf.js";
10
- import { u as projectBoundedCodexThreadHistory } from "./transcript-mirror-BU0_LKAL.js";
1
+ import { E as resolveCodexPluginsPolicy, F as resolveCodexAppServerLocalHomeDir, M as CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME, P as resolveCodexAppServerHomeDir, f as assertCodexModelBackedReviewerEffectiveConfig, j as CODEX_PLUGINS_MARKETPLACE_NAME, n as codexSandboxPolicyForTurn } from "./config-CcYL77O6.js";
2
+ import { $ as CodexAppServerRpcError, D as CodexThreadStartRequestError, E as CodexThreadBindingConflictError, H as getCodexAppServerClientInstanceId, Ht as isJsonObject, J as isCodexAppServerOverloadError, Q as resolveCodexAppServerClientInstanceId, St as releaseCodexAppServerLiveThread, T as CodexRestrictedToolSurfaceAttestationError, Vt as flattenCodexDynamicToolFunctions, _ as retainSharedCodexAppServerClientByInstanceId, bt as isCodexAppServerLiveThreadClaimed, dt as resolveCodexToolAbortTerminalReason, gt as consumeCodexAppServerLiveThread, n as captureExclusiveSharedCodexAppServerClient, w as CodexAdoptedThreadActiveError, wt as unsubscribeCodexAppServerLiveThread, yt as isCodexAppServerClientRuntimeLive } from "./shared-client-B1tT5U6r.js";
3
+ import { a as hashCodexAppServerBindingFingerprint, c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, i as createCodexSessionGenerationSupersededError, n as assertCodexBindingMayBeReplaced, o as isCodexAppServerNativeAuthProfile, s as normalizeCodexAppServerBindingModelProvider } from "./session-binding-D0zinCP1.js";
4
+ import { a as assertCodexThreadForkResponse, r as assertCodexThreadAcceptsDirectInput, s as assertCodexThreadStartResponse, t as CodexThreadDirectInputError } from "./protocol-validators-Bg5rpNxk.js";
5
+ import { a as closeCodexStartupClientBestEffort, c as isCodexAppServerUnsafeSubscriptionError, f as unsubscribeCodexThreadBestEffort, n as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, r as CodexAppServerUnsafeSubscriptionError } from "./attempt-client-cleanup-CpVfwctT.js";
6
+ import { c as serializeCodexAppInventoryError, n as buildCodexAppServerConnectionFingerprint, s as defaultCodexAppInventoryCache, t as resumeCodexAppServerThread } from "./thread-resume-BFt1-ZDa.js";
7
+ import { c as withExclusiveCodexAppServerThread, l as isIncognitoSessionKey } from "./thread-ownership-RkaoZCmQ.js";
8
+ import { i as readCodexSessionMeta, n as codexCatalogHomeId } from "./session-catalog-home-id-Dt2GiG5o.js";
9
+ import { f as projectBoundedCodexThreadHistory } from "./transcript-mirror-1ep4XjzF.js";
11
10
  import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-CnfoUPCE.js";
12
11
  import { asOptionalRecord, isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
13
12
  import fs from "node:fs/promises";
14
13
  import path from "node:path";
15
- import { SKILL_WORKSHOP_TOOL_NAME, TRANSCRIPT_CREDENTIAL_SAFETY_PROMPT, buildDelegationGuidanceSection, buildHarnessVisibleReplyGuidance, buildSkillWorkshopPromptSection, embeddedAgentLog, formatErrorMessage, isActiveHarnessContextEngine, isHostScopedAgentToolActive, resolveMainSessionDelegationMode } from "openclaw/plugin-sdk/agent-harness-runtime";
14
+ import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
15
+ import { addTimerTimeoutGraceMs, finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
16
+ import { SKILL_WORKSHOP_TOOL_NAME, buildCredentialSafetyPrompt, buildDelegationGuidanceSection, buildHarnessVisibleReplyGuidance, buildSkillWorkshopPromptSection, embeddedAgentLog, formatErrorMessage, isActiveHarnessContextEngine, isHostScopedAgentToolActive, resolveMainSessionDelegationMode } from "openclaw/plugin-sdk/agent-harness-runtime";
16
17
  import * as crypto$1 from "node:crypto";
17
18
  import crypto, { createHash } from "node:crypto";
18
19
  import os from "node:os";
19
- import { resolveAgentDir as resolveAgentDir$1, resolveSessionAgentIds as resolveSessionAgentIds$1 } from "openclaw/plugin-sdk/agent-runtime";
20
+ import { isDeepStrictEqual } from "node:util";
21
+ import { resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
22
+ import { resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
20
23
  import { z } from "zod";
21
24
  import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
22
25
  import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
26
+ import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
27
+ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
23
28
  import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
24
29
  import { isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
25
30
  import { resolveRequiredHomeDir, resolveStateDir } from "openclaw/plugin-sdk/state-paths";
26
31
  import { listRegisteredPluginAgentPromptGuidance } from "openclaw/plugin-sdk/plugin-runtime";
27
32
  import { buildHostnameAllowlistPolicyFromSuffixAllowlist } from "openclaw/plugin-sdk/ssrf-policy";
28
- import { buildCodexUserMcpServersThreadConfigPatchForRuntime } from "openclaw/plugin-sdk/codex-mcp-projection";
29
- import { isDiagnosticFlagEnabled } from "openclaw/plugin-sdk/diagnostic-runtime";
30
- import { GPT5_HEARTBEAT_PROMPT_OVERLAY } from "openclaw/plugin-sdk/provider-model-shared";
33
+ import { buildCodexUserMcpServersThreadConfigPatchForRun } from "openclaw/plugin-sdk/codex-mcp-projection";
34
+ import { registerRetainedNativeHookRelayForBundledRuntime } from "openclaw/plugin-sdk/native-hook-relay-runtime";
35
+ import { isDiagnosticFlagEnabled } from "openclaw/plugin-sdk/diagnostic-flags";
31
36
  //#region extensions/codex/src/app-server/plugin-inventory.ts
32
37
  /**
33
38
  * Reads Codex plugin marketplace state and app inventory to decide which
@@ -270,15 +275,39 @@ function resolveOwnedApps(params) {
270
275
  enabled: false,
271
276
  needsAuth: true
272
277
  };
273
- return {
278
+ return Object.assign({
274
279
  id: app.id,
275
280
  name: app.name,
276
281
  accessible: info.isAccessible,
277
282
  enabled: info.isEnabled,
278
283
  needsAuth: !info.isAccessible
279
- };
284
+ }, resolveOwnedAppReadOnlyToolConfigKeys(info));
280
285
  }).toSorted((left, right) => left.id.localeCompare(right.id));
281
286
  }
287
+ /** Returns the config keys that Codex metadata proves belong to read-only tools. */
288
+ function resolveOwnedAppReadOnlyToolConfigKeys(app) {
289
+ const appName = app.name.trim();
290
+ const appNameLower = appName.toLowerCase();
291
+ const tools = app.toolSummaries ?? [];
292
+ const writableToolConfigKeys = new Set(tools.filter((tool) => !tool.isReadOnly).flatMap((tool) => resolveAppToolConfigKeys({
293
+ appName,
294
+ appNameLower,
295
+ tool
296
+ })));
297
+ const keys = tools.filter((tool) => tool.isReadOnly).flatMap((tool) => resolveAppToolConfigKeys({
298
+ appName,
299
+ appNameLower,
300
+ tool
301
+ })).filter((key) => !writableToolConfigKeys.has(key));
302
+ return keys.length > 0 ? { readOnlyToolConfigKeys: Array.from(new Set(keys)).toSorted() } : {};
303
+ }
304
+ function resolveAppToolConfigKeys(params) {
305
+ const keys = [params.tool.name];
306
+ if (params.tool.title) keys.push(params.tool.title);
307
+ if (params.appName) keys.push(`${params.appName}_${params.tool.name}`);
308
+ if (params.appNameLower && params.appNameLower !== params.appName) keys.push(`${params.appNameLower}_${params.tool.name}`);
309
+ return keys;
310
+ }
282
311
  function findPluginSummary(marketplace, pluginName) {
283
312
  const exact = marketplace.plugins.find((plugin) => plugin.id === pluginName || plugin.id === `${pluginName}@${marketplace.name}`);
284
313
  if (exact) return exact;
@@ -457,12 +486,21 @@ function activationFailure(identity, reason, diagnostic, extraDiagnostics = [])
457
486
  }
458
487
  //#endregion
459
488
  //#region extensions/codex/src/app-server/plugin-thread-app-admission.ts
489
+ function resolveCodexPluginThreadAppCacheKey(params) {
490
+ return params.threadId ? `${params.appCacheKey}:thread:${encodeURIComponent(params.threadId)}` : params.appCacheKey;
491
+ }
492
+ function createCodexPluginThreadAppInventoryRequest(params) {
493
+ return async (method, requestParams) => await params.request(method, (method === "app/installed" || method === "app/read") && params.threadId ? {
494
+ ...requestParams,
495
+ threadId: params.threadId
496
+ } : requestParams);
497
+ }
460
498
  async function refreshCodexPluginAppInventory(params, appCache, options = {}) {
461
499
  if (!params.appCacheKey) return;
462
- const request = async (method, requestParams) => await params.request(method, requestParams);
500
+ const request = createCodexPluginThreadAppInventoryRequest(params);
463
501
  try {
464
502
  return await appCache.refreshNow({
465
- key: params.appCacheKey,
503
+ key: resolveCodexPluginThreadAppCacheKey(params),
466
504
  request,
467
505
  nowMs: params.nowMs,
468
506
  forceRefetch: options.forceRefetch,
@@ -499,9 +537,9 @@ function normalizeCodexPluginOwnerName(name) {
499
537
  return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
500
538
  }
501
539
  async function readCodexThreadAdmissibleAccountApps(params, appCache) {
502
- const request = async (method, requestParams) => await params.request(method, requestParams);
540
+ const request = createCodexPluginThreadAppInventoryRequest(params);
503
541
  const cachedInventory = appCache.read({
504
- key: params.appCacheKey,
542
+ key: resolveCodexPluginThreadAppCacheKey(params),
505
543
  request,
506
544
  nowMs: params.nowMs,
507
545
  suppressRefresh: true
@@ -527,7 +565,8 @@ function toCodexPluginOwnedAccountApp(app) {
527
565
  name: app.name,
528
566
  accessible: app.isAccessible,
529
567
  enabled: app.isEnabled,
530
- needsAuth: !app.isAccessible
568
+ needsAuth: !app.isAccessible,
569
+ ...resolveOwnedAppReadOnlyToolConfigKeys(app)
531
570
  };
532
571
  }
533
572
  function resolveCodexThreadConfigAppsForRecord(params) {
@@ -616,6 +655,11 @@ function buildCodexPluginThreadConfigTimeoutFallback(params) {
616
655
  /** Builds the Codex apps config patch and policy context for a native thread. */
617
656
  async function buildCodexPluginThreadConfig(params) {
618
657
  const appCache = params.appCache ?? defaultCodexAppInventoryCache;
658
+ const threadAppCacheKey = resolveCodexPluginThreadAppCacheKey(params);
659
+ const threadRequest = (method, requestParams) => params.request(method, (method === "app/installed" || method === "app/read") && params.threadId && isJsonObject(requestParams) ? {
660
+ ...requestParams,
661
+ threadId: params.threadId
662
+ } : requestParams);
619
663
  let inputFingerprint = buildCodexPluginThreadConfigInputFingerprint({
620
664
  pluginConfig: params.pluginConfig,
621
665
  appCacheKey: params.appCacheKey
@@ -629,9 +673,9 @@ async function buildCodexPluginThreadConfig(params) {
629
673
  let inventory = policy.pluginPolicies.length > 0 ? await readCodexPluginInventory({
630
674
  pluginConfig: params.pluginConfig,
631
675
  policy,
632
- request: params.request,
676
+ request: threadRequest,
633
677
  appCache,
634
- appCacheKey: params.appCacheKey,
678
+ appCacheKey: threadAppCacheKey,
635
679
  configCwd: params.configCwd,
636
680
  metadataCache: params.metadataCache,
637
681
  nowMs: params.nowMs,
@@ -647,9 +691,9 @@ async function buildCodexPluginThreadConfig(params) {
647
691
  inventory = await readCodexPluginInventory({
648
692
  pluginConfig: params.pluginConfig,
649
693
  policy,
650
- request: params.request,
694
+ request: threadRequest,
651
695
  appCache,
652
- appCacheKey: params.appCacheKey,
696
+ appCacheKey: threadAppCacheKey,
653
697
  configCwd: params.configCwd,
654
698
  metadataCache: params.metadataCache,
655
699
  nowMs: params.nowMs
@@ -665,9 +709,9 @@ async function buildCodexPluginThreadConfig(params) {
665
709
  if (!record.activationRequired) continue;
666
710
  const activation = await ensureCodexPluginActivation({
667
711
  identity: record.policy,
668
- request: params.request,
712
+ request: threadRequest,
669
713
  appCache,
670
- appCacheKey: params.appCacheKey,
714
+ appCacheKey: threadAppCacheKey,
671
715
  configCwd: params.configCwd,
672
716
  metadataCache: params.metadataCache,
673
717
  deferAppInventoryRefresh: true,
@@ -691,9 +735,9 @@ async function buildCodexPluginThreadConfig(params) {
691
735
  inventory = await readCodexPluginInventory({
692
736
  pluginConfig: params.pluginConfig,
693
737
  policy,
694
- request: params.request,
738
+ request: threadRequest,
695
739
  appCache,
696
- appCacheKey: params.appCacheKey,
740
+ appCacheKey: threadAppCacheKey,
697
741
  configCwd: params.configCwd,
698
742
  metadataCache: params.metadataCache,
699
743
  nowMs: params.nowMs
@@ -712,9 +756,9 @@ async function buildCodexPluginThreadConfig(params) {
712
756
  inventory = await readCodexPluginInventory({
713
757
  pluginConfig: params.pluginConfig,
714
758
  policy,
715
- request: params.request,
759
+ request: threadRequest,
716
760
  appCache,
717
- appCacheKey: params.appCacheKey,
761
+ appCacheKey: threadAppCacheKey,
718
762
  configCwd: params.configCwd,
719
763
  metadataCache: params.metadataCache,
720
764
  nowMs: params.nowMs
@@ -952,7 +996,7 @@ function readPersistedAppToolApprovalOverrideNames(config, app) {
952
996
  const appConfig = isJsonObject(appsRoot) ? appsRoot[app.id] : void 0;
953
997
  const tools = isJsonObject(appConfig) ? appConfig.tools : void 0;
954
998
  if (!isJsonObject(tools)) return [];
955
- return Object.entries(tools).filter(([, value]) => hasPersistedToolApprovalOverride(value)).map(([toolName]) => toolName).toSorted();
999
+ return Object.entries(tools).filter(([toolName]) => !app.readOnlyToolConfigKeys?.includes(toolName)).filter(([, value]) => hasPersistedToolApprovalOverride(value)).map(([toolName]) => toolName).toSorted();
956
1000
  }
957
1001
  function hasPersistedToolApprovalOverride(value) {
958
1002
  return isJsonObject(value) && value.approval_mode !== void 0;
@@ -1154,6 +1198,7 @@ function renderMessagesForCodexContext(messages, options) {
1154
1198
  }).filter((value) => Boolean(value)).join("\n\n");
1155
1199
  }
1156
1200
  function renderMessageBody(message, options) {
1201
+ if (message.role === "compactionSummary" || message.role === "branchSummary") return truncateText(message.summary.trim(), options.maxTextPartChars);
1157
1202
  if (!hasMessageContent(message)) return "";
1158
1203
  if (typeof message.content === "string") return truncateText(message.content.trim(), options.maxTextPartChars);
1159
1204
  if (!Array.isArray(message.content)) return "[non-text content omitted]";
@@ -1622,7 +1667,8 @@ async function collectPersonalSkillRealPaths(homes, codexHome) {
1622
1667
  }
1623
1668
  const configuredCodexHome = codexHome?.trim() || process.env.CODEX_HOME?.trim();
1624
1669
  if (configuredCodexHome) {
1625
- const stateOwned = isPathInside(realStateDir, await canonicalizeExistingPath(configuredCodexHome));
1670
+ const realCodexHome = await canonicalizeExistingPath(configuredCodexHome);
1671
+ const stateOwned = isPathInside(realStateDir, realCodexHome);
1626
1672
  roots.push({
1627
1673
  dir: path.join(configuredCodexHome, "skills"),
1628
1674
  onlyEscapedStateTargets: stateOwned
@@ -1685,12 +1731,14 @@ async function collectPersonalSkillRealPaths(homes, codexHome) {
1685
1731
  try {
1686
1732
  const stat = await fs.stat(entryPath);
1687
1733
  if (entry.name === "SKILL.md" && stat.isFile()) await recordSkillFile(entryPath, current.onlyEscapedStateTargets);
1688
- else if (stat.isDirectory()) if (current.depth < MAX_PERSONAL_SKILL_DEPTH) queue.push({
1689
- dir: entryPath,
1690
- depth: current.depth + 1,
1691
- onlyEscapedStateTargets: current.onlyEscapedStateTargets
1692
- });
1693
- else complete = false;
1734
+ else if (stat.isDirectory()) {
1735
+ if (current.depth < MAX_PERSONAL_SKILL_DEPTH) queue.push({
1736
+ dir: entryPath,
1737
+ depth: current.depth + 1,
1738
+ onlyEscapedStateTargets: current.onlyEscapedStateTargets
1739
+ });
1740
+ else complete = false;
1741
+ }
1694
1742
  } catch (error) {
1695
1743
  if (!isMissingPathError(error)) complete = false;
1696
1744
  }
@@ -1780,33 +1828,6 @@ function applyCodexNativeSkillIsolation(config, isolation) {
1780
1828
  };
1781
1829
  }
1782
1830
  //#endregion
1783
- //#region extensions/codex/src/app-server/thread-lifecycle-errors.ts
1784
- var CodexThreadStartRequestError = class extends Error {
1785
- constructor(cause) {
1786
- super(`thread/start: ${formatErrorMessage(cause)}`, { cause });
1787
- this.name = "CodexThreadStartRequestError";
1788
- }
1789
- };
1790
- var CodexThreadBindingConflictError = class extends Error {
1791
- constructor(threadId, operation) {
1792
- super(`Codex thread binding changed while ${operation}: ${threadId}`);
1793
- this.name = "CodexThreadBindingConflictError";
1794
- }
1795
- };
1796
- var CodexRestrictedToolSurfaceAttestationError = class extends Error {
1797
- constructor(cause) {
1798
- super("Codex restricted-tool-surface MCP attestation failed", { cause });
1799
- this.name = "CodexRestrictedToolSurfaceAttestationError";
1800
- }
1801
- };
1802
- var CodexThreadBindingConflictAfterCleanupError = class extends CodexThreadBindingConflictError {};
1803
- var CodexAdoptedThreadActiveError = class extends Error {
1804
- constructor() {
1805
- super("Codex session became active in another runner; wait for it to finish before continuing");
1806
- this.name = "CodexAdoptedThreadActiveError";
1807
- }
1808
- };
1809
- //#endregion
1810
1831
  //#region extensions/codex/src/app-server/managed-thread-store.ts
1811
1832
  const CODEX_MANAGED_THREAD_NAMESPACE = "app-server-managed-threads";
1812
1833
  const CODEX_MANAGED_THREAD_MAX_ENTRIES = 2e4;
@@ -1835,6 +1856,10 @@ function managedThreadStoreKey(sourceHomeId, threadId) {
1835
1856
  /** Durable ownership index for Codex threads created by OpenClaw. */
1836
1857
  function createCodexManagedThreadStore(state) {
1837
1858
  return {
1859
+ async has(sourceHomeId, threadId) {
1860
+ const parsed = managedThreadSchema.safeParse(state.lookup(managedThreadStoreKey(sourceHomeId, threadId)));
1861
+ return parsed.success && parsed.data.sourceHomeId === sourceHomeId && parsed.data.threadId === threadId;
1862
+ },
1838
1863
  async mark(params) {
1839
1864
  try {
1840
1865
  const value = managedThreadSchema.parse({
@@ -1981,11 +2006,6 @@ const LEGACY_PRO_REASONING_EFFORTS = [
1981
2006
  ];
1982
2007
  const LEGACY_PRO_MODEL_ID_RE = /^gpt-5\.[45]-pro$/u;
1983
2008
  const MODERN_GPT_5_MODEL_ID_RE = /^gpt-5\.(?:[3-9]|[1-9]\d)(?:$|-)/u;
1984
- function normalizeCodexReasoningEfforts(efforts) {
1985
- if (!efforts) return [];
1986
- const supported = new Set(efforts.map((effort) => effort.trim().toLowerCase()));
1987
- return CODEX_REASONING_EFFORTS.filter((effort) => supported.has(effort));
1988
- }
1989
2009
  /** Read reasoning metadata after the Codex app-server route has been selected. */
1990
2010
  function readCodexSupportedReasoningEfforts(compat) {
1991
2011
  if (!compat || typeof compat !== "object" || Array.isArray(compat)) return;
@@ -1994,7 +2014,8 @@ function readCodexSupportedReasoningEfforts(compat) {
1994
2014
  return efforts.filter((effort) => typeof effort === "string");
1995
2015
  }
1996
2016
  function resolveSupportedReasoningEffort(params) {
1997
- const supported = normalizeCodexReasoningEfforts(params.supportedReasoningEfforts);
2017
+ const declared = new Set(params.supportedReasoningEfforts.map((effort) => effort.trim().toLowerCase()));
2018
+ const supported = CODEX_REASONING_EFFORTS.filter((effort) => declared.has(effort));
1998
2019
  if (supported.includes(params.requested)) return params.requested;
1999
2020
  const fallbackEfforts = params.requested === "ultra" ? supported : supported.filter((effort) => effort !== "ultra");
2000
2021
  const requestedRank = CODEX_REASONING_EFFORTS.indexOf(params.requested);
@@ -2002,7 +2023,8 @@ function resolveSupportedReasoningEffort(params) {
2002
2023
  }
2003
2024
  /** Resolve a turn effort from the selected model's provider-owned metadata. */
2004
2025
  function resolveCodexAppServerReasoningEffort(params) {
2005
- if (params.thinkLevel === "off" || params.thinkLevel === "adaptive") return null;
2026
+ if (params.thinkLevel === "off") return params.supportedReasoningEfforts?.includes("none") ? "none" : null;
2027
+ if (params.thinkLevel === "adaptive") return null;
2006
2028
  if (params.supportedReasoningEfforts) return resolveSupportedReasoningEffort({
2007
2029
  requested: params.thinkLevel,
2008
2030
  supportedReasoningEfforts: params.supportedReasoningEfforts
@@ -2093,7 +2115,7 @@ function resolveReasoningEffort(thinkLevel, modelId, supportedReasoningEfforts)
2093
2115
  }
2094
2116
  //#endregion
2095
2117
  //#region extensions/codex/src/app-server/project-doc-thread-config.ts
2096
- const CODEX_NATIVE_PROJECT_DOC_MAX_BYTES = 128 * 1024;
2118
+ const CODEX_NATIVE_PROJECT_DOC_MAX_BYTES = 131072;
2097
2119
  function buildCodexProjectDocThreadConfig(config) {
2098
2120
  const defaults = { project_doc_max_bytes: CODEX_NATIVE_PROJECT_DOC_MAX_BYTES };
2099
2121
  return mergeCodexThreadConfigs(defaults, config) ?? defaults;
@@ -2102,6 +2124,7 @@ function buildCodexProjectDocThreadConfig(config) {
2102
2124
  //#region extensions/codex/src/app-server/thread-prompt.ts
2103
2125
  function buildDeveloperInstructions(params, options = {}) {
2104
2126
  const deferredToolNames = /* @__PURE__ */ new Set();
2127
+ let secretsToolName;
2105
2128
  let hasSkillWorkshop = false;
2106
2129
  let hasSessionsSpawn = false;
2107
2130
  let hasSessionsYield = false;
@@ -2115,6 +2138,7 @@ function buildDeveloperInstructions(params, options = {}) {
2115
2138
  for (const tool of spec.type === "namespace" ? spec.tools : [spec]) {
2116
2139
  const name = tool.name.trim();
2117
2140
  if (tool.deferLoading === true && name) deferredToolNames.add(name);
2141
+ if (name === "secrets" && params.disableTools !== true) secretsToolName ??= spec.type === "namespace" ? `${spec.name}.${name}` : name;
2118
2142
  hasSkillWorkshop ||= name === SKILL_WORKSHOP_TOOL_NAME;
2119
2143
  hasSessionsSpawn ||= name === "sessions_spawn";
2120
2144
  hasSessionsYield ||= isDirectNamespace && name === "sessions_yield";
@@ -2154,7 +2178,7 @@ function buildDeveloperInstructions(params, options = {}) {
2154
2178
  sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
2155
2179
  messageToolAvailable
2156
2180
  }),
2157
- TRANSCRIPT_CREDENTIAL_SAFETY_PROMPT,
2181
+ buildCredentialSafetyPrompt(secretsToolName),
2158
2182
  nativeCommandGuidance,
2159
2183
  params.extraSystemPrompt
2160
2184
  ].filter((section) => typeof section === "string" && section.trim()).join("\n\n");
@@ -2261,7 +2285,8 @@ function resolveCodexWebSearchPlan(params) {
2261
2285
  const CODEX_CODE_MODE_THREAD_CONFIG = {
2262
2286
  "features.code_mode": true,
2263
2287
  "features.code_mode_only": false,
2264
- "features.apply_patch_streaming_events": true
2288
+ "features.apply_patch_streaming_events": true,
2289
+ suppress_unstable_features_warning: true
2265
2290
  };
2266
2291
  const CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG = { "features.goals": false };
2267
2292
  const CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG = { "tools.update_plan.enabled": false };
@@ -2451,33 +2476,24 @@ function buildCodexRuntimeThreadConfig(config, options = {}) {
2451
2476
  "features.code_mode_only": options.nativeCodeModeOnlyEnabled === true
2452
2477
  };
2453
2478
  if (options.nativeCodeModeEnabled === false) {
2454
- const disabledConfig = mergeCodexThreadConfigs(configured, CODEX_CODE_MODE_DISABLED_THREAD_CONFIG, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG) ?? {
2455
- ...CODEX_CODE_MODE_DISABLED_THREAD_CONFIG,
2456
- ...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG,
2457
- ...CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG
2458
- };
2479
+ const disabledConfig = expectDefined(mergeCodexThreadConfigs(configured, CODEX_CODE_MODE_DISABLED_THREAD_CONFIG, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG), "Codex disabled code mode config");
2459
2480
  delete disabledConfig["features.apply_patch_streaming_events"];
2460
2481
  return disabledConfig;
2461
2482
  }
2462
- if (options.nativeCodeModeOnlyEnabled === true) return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, configured, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG, { "features.code_mode_only": true }) ?? {
2463
- ...codeModeConfig,
2464
- ...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG,
2465
- ...CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG,
2466
- "features.code_mode_only": true
2467
- }, options.directOnlyToolNamespaces);
2468
- return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, configured, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG) ?? {
2469
- ...codeModeConfig,
2470
- ...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG,
2471
- ...CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG
2472
- }, options.directOnlyToolNamespaces);
2483
+ if (options.nativeCodeModeOnlyEnabled === true) return ensureDirectOnlyToolNamespaces(expectDefined(mergeCodexThreadConfigs(codeModeConfig, configured, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG, { "features.code_mode_only": true }), "Codex code mode only config"), options.directOnlyToolNamespaces);
2484
+ return ensureDirectOnlyToolNamespaces(expectDefined(mergeCodexThreadConfigs(codeModeConfig, configured, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG), "Codex code mode config"), options.directOnlyToolNamespaces);
2473
2485
  }
2474
2486
  function ensureDirectOnlyToolNamespaces(config, requiredNamespaces) {
2475
2487
  if (!requiredNamespaces?.length) return config;
2476
- const configured = config["code_mode.direct_only_tool_namespaces"];
2477
- const namespaces = Array.isArray(configured) ? configured.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
2488
+ const feature = expectDefined(config["features.code_mode"], "Codex code mode config");
2489
+ const configured = isJsonObject(feature) ? feature : { enabled: feature };
2490
+ const namespaces = Array.isArray(configured.direct_only_tool_namespaces) ? configured.direct_only_tool_namespaces.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
2478
2491
  return {
2479
2492
  ...config,
2480
- "code_mode.direct_only_tool_namespaces": [.../* @__PURE__ */ new Set([...namespaces, ...requiredNamespaces])]
2493
+ "features.code_mode": {
2494
+ ...configured,
2495
+ direct_only_tool_namespaces: [.../* @__PURE__ */ new Set([...namespaces, ...requiredNamespaces])]
2496
+ }
2481
2497
  };
2482
2498
  }
2483
2499
  function resolveDirectOnlyToolNamespaces(dynamicTools) {
@@ -2491,10 +2507,6 @@ function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
2491
2507
  const configMcpServers = config?.mcp_servers;
2492
2508
  if (restrictedToolSurface && configMcpServers !== void 0 && !isJsonObject(configMcpServers)) throw new Error("Codex restricted tool surface received invalid thread mcp_servers config");
2493
2509
  const restrictedToolSurfaceMcpServerNames = [...options.restrictedToolSurfaceInheritedMcpServerNames ?? [], ...isJsonObject(configMcpServers) ? Object.keys(configMcpServers) : []];
2494
- const restrictedRunConfig = restrictedToolSurface && isJsonObject(configMcpServers) ? {
2495
- ...config,
2496
- mcp_servers: {}
2497
- } : config;
2498
2510
  const webSearchConfig = resolveCodexWebSearchPlan({
2499
2511
  config: params.config,
2500
2512
  disableTools: params.disableTools,
@@ -2502,7 +2514,7 @@ function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
2502
2514
  nativeProviderWebSearchSupport: options.nativeProviderWebSearchSupport,
2503
2515
  webSearchAllowed: options.webSearchAllowed
2504
2516
  }).threadConfig;
2505
- const baseConfig = buildCodexRuntimeThreadConfig(mergeCodexThreadConfigs(restrictedRunConfig, webSearchConfig), options);
2517
+ const baseConfig = buildCodexRuntimeThreadConfig(mergeCodexThreadConfigs(config, webSearchConfig), options);
2506
2518
  return applyCodexManagedShellEnvironment({
2507
2519
  ...mergeCodexThreadConfigs(baseConfig, options.appServer?.networkProxy?.configPatch, params.pluginHarnessToolPolicySafeDeniedTools?.includes("image_generate") ? { "features.image_generation": false } : void 0, shouldDisableCodexToolSearchForModel(params.modelId) ? CODEX_TOOL_SEARCH_UNSUPPORTED_THREAD_CONFIG : void 0, params.delegationCapability === "report_only" ? CODEX_DELEGATION_DISABLED_THREAD_CONFIG : void 0, messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted === true ? buildRestrictedToolConfigPatch(restrictedToolSurfaceMcpServerNames) : buildCodexRingZeroThreadConfigPatch(params, options.hostSystemAgentActive, restrictedToolSurfaceMcpServerNames), restrictedTurnDisablesProjectDocs ? CODEX_NO_PROJECT_DOCS_CONFIG : void 0, params.authoredContextTokenCap === void 0 ? void 0 : { model_context_window: params.authoredContextTokenCap }) ?? baseConfig,
2508
2520
  ...params.bootstrapContextMode === "lightweight" ? CODEX_NO_PROJECT_DOCS_CONFIG : {}
@@ -2613,14 +2625,13 @@ function resolveCodexThreadEnvironmentSelection(options) {
2613
2625
  }
2614
2626
  //#endregion
2615
2627
  //#region extensions/codex/src/app-server/thread-lifecycle-io.ts
2616
- function resolveManagedThreadSourceHomeId(params) {
2617
- const agentId = resolveSessionAgentIds$1({
2628
+ function resolveCodexThreadAgentDir(params) {
2629
+ const agentId = resolveSessionAgentIdsStrict({
2618
2630
  config: params.params.config,
2619
2631
  sessionKey: params.params.sessionKey,
2620
2632
  agentId: params.agentId ?? params.params.agentId
2621
2633
  }).sessionAgentId;
2622
- const agentDir = params.agentDir ?? params.params.agentDir ?? resolveAgentDir$1(params.params.config ?? {}, agentId);
2623
- return codexCatalogHomeId(resolveCodexAppServerLocalHomeDir(params.appServer.start, agentDir));
2634
+ return params.agentDir ?? params.params.agentDir ?? resolveAgentDir$1(params.params.config ?? {}, agentId);
2624
2635
  }
2625
2636
  function resolveCodexThreadRolloutPath(thread) {
2626
2637
  const rolloutPath = thread.path?.trim();
@@ -2630,6 +2641,9 @@ function resolveCodexThreadRolloutPath(thread) {
2630
2641
  async function resumeExistingCodexThread(params, context) {
2631
2642
  const { binding: resumeBinding, bindingIdentity, startModelSelection, startModelProvider, userMcpServersConfigPatch, dynamicToolsFingerprint, dynamicToolsContainDeferred, webSearchThreadConfigFingerprint, nativeSkillIsolationFingerprint, userMcpServersFingerprint, ringZeroConfigFingerprint, ringZeroClientInstanceId, networkProxyConfigFingerprint, contextEngineBinding, environmentSelectionFingerprint, hostSystemAgentActive, ringZeroActive, restrictedToolSurface, restrictedToolSurfaceInheritedMcpServerNames, nativeSkillIsolation, lifecycleTiming, normalizeBindingModelProvider, throwIfAborted, clearCurrentBinding } = context;
2632
2643
  let resumeReservation;
2644
+ let resumeResponseAccepted = false;
2645
+ let checkingLoadedPluginThreadConfig = false;
2646
+ const abandonClient = params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client));
2633
2647
  try {
2634
2648
  const authProfileId = resumeBinding.connectionScope === "supervision" ? void 0 : params.params.authProfileId ?? resumeBinding.authProfileId;
2635
2649
  const finalConfigPatch = context.prebuiltFinalConfigPatch ?? params.buildFinalConfigPatch?.({
@@ -2639,7 +2653,8 @@ async function resumeExistingCodexThread(params, context) {
2639
2653
  configPatch: params.finalConfigPatch,
2640
2654
  nativeHookRelayGeneration: params.nativeHookRelayGeneration
2641
2655
  };
2642
- const pluginAppsConfigPatch = context.prebuiltPluginThreadConfig?.configPatch ?? (params.pluginThreadConfig?.enabled && resumeBinding.pluginAppPolicyContext ? buildCodexPluginAppsConfigPatchFromPolicyContext(resumeBinding.pluginAppPolicyContext) : void 0);
2656
+ const pluginThreadConfig = context.prebuiltPluginThreadConfig ?? (params.pluginThreadConfig?.requiresCurrentPolicyCheck ? await lifecycleTiming.measure("plugin-config-build", () => params.pluginThreadConfig?.build()) : void 0);
2657
+ const pluginAppsConfigPatch = pluginThreadConfig?.configPatch ?? (params.pluginThreadConfig?.enabled && resumeBinding.pluginAppPolicyContext ? buildCodexPluginAppsConfigPatchFromPolicyContext(resumeBinding.pluginAppPolicyContext) : void 0);
2643
2658
  const resumeConfig = applyCodexNativeSkillIsolation(mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, finalConfigPatch.configPatch), nativeSkillIsolation);
2644
2659
  const resumeParams = lifecycleTiming.measureSync("thread-resume-params", () => buildThreadResumeParams(params.params, {
2645
2660
  threadId: resumeBinding.threadId,
@@ -2663,25 +2678,36 @@ async function resumeExistingCodexThread(params, context) {
2663
2678
  }));
2664
2679
  const requestModelProvider = typeof resumeParams.modelProvider === "string" && resumeParams.modelProvider.trim() ? resumeParams.modelProvider : void 0;
2665
2680
  throwIfAborted();
2666
- if (resumeBinding.preserveNativeModel === true) {
2667
- const current = await lifecycleTiming.measure("thread-read-adoption-status", () => params.client.request("thread/read", {
2668
- threadId: resumeBinding.threadId,
2669
- includeTurns: false
2670
- }, { signal: params.signal }));
2671
- throwIfAborted();
2672
- if (current.thread.status?.type === "active") throw new CodexAdoptedThreadActiveError();
2673
- }
2674
2681
  resumeReservation = params.reserveResumeThread?.(resumeBinding.threadId);
2675
2682
  const response = await lifecycleTiming.measure("thread-resume-request", () => resumeCodexAppServerThread({
2676
2683
  client: params.client,
2677
- abandonClient: params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)),
2684
+ abandonClient,
2678
2685
  request: resumeParams,
2679
- signal: params.signal
2686
+ signal: params.signal,
2687
+ assertCurrent: context.assertResumeOwnership,
2688
+ isPrewriteOwnershipError: (error) => error instanceof CodexAdoptedThreadActiveError
2680
2689
  }));
2690
+ resumeResponseAccepted = true;
2691
+ assertCodexThreadAcceptsDirectInput(response.thread);
2692
+ context.assertResumeConfiguration?.();
2693
+ checkingLoadedPluginThreadConfig = true;
2694
+ const loadedPluginThreadConfig = await context.buildLoadedPluginThreadConfig?.(resumeBinding);
2695
+ if (loadedPluginThreadConfig && loadedPluginThreadConfig.fingerprint !== (pluginThreadConfig?.fingerprint ?? resumeBinding.pluginAppsFingerprint)) {
2696
+ checkingLoadedPluginThreadConfig = false;
2697
+ throw new Error("Codex thread app policy changed; a fresh thread configuration is required");
2698
+ }
2699
+ await attestCodexPluginThreadApps({
2700
+ client: params.client,
2701
+ threadId: response.thread.id,
2702
+ appIds: loadedPluginThreadConfig?.provisionalAppIds ?? pluginThreadConfig?.provisionalAppIds ?? [],
2703
+ signal: params.signal
2704
+ });
2705
+ checkingLoadedPluginThreadConfig = false;
2681
2706
  if (ringZeroActive || isMessageOnlyCodexSourceReply(params.params) || params.params.pluginHarnessToolPolicyRestricted === true) try {
2682
2707
  await lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, response.thread.id, resumeParams.config, params.signal));
2683
2708
  } catch (error) {
2684
- await (params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)))();
2709
+ context.assertResumeOwnership?.();
2710
+ await abandonClient();
2685
2711
  throw new CodexRestrictedToolSurfaceAttestationError(error);
2686
2712
  }
2687
2713
  throwIfAborted();
@@ -2689,6 +2715,7 @@ async function resumeExistingCodexThread(params, context) {
2689
2715
  const nextMcpServersFingerprint = params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : resumeBinding.mcpServersFingerprint;
2690
2716
  const resumePatch = {
2691
2717
  clientId: resolveCodexAppServerClientInstanceId(params.client),
2718
+ pendingResumeConfiguration: void 0,
2692
2719
  cwd: params.cwd,
2693
2720
  rolloutPath: resolveCodexThreadRolloutPath(response.thread) ?? resumeBinding.rolloutPath,
2694
2721
  authProfileId: boundAuthProfileId,
@@ -2709,9 +2736,9 @@ async function resumeExistingCodexThread(params, context) {
2709
2736
  networkProxyConfigFingerprint,
2710
2737
  nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration ?? resumeBinding.nativeHookRelayGeneration,
2711
2738
  appServerRuntimeFingerprint: resumeBinding.connectionScope === "supervision" ? buildCodexAppServerConnectionFingerprint(params.appServer, params.params.agentDir) : params.appServerRuntimeFingerprint,
2712
- pluginAppsFingerprint: context.prebuiltPluginThreadConfig?.fingerprint ?? resumeBinding.pluginAppsFingerprint,
2713
- pluginAppsInputFingerprint: context.prebuiltPluginThreadConfig?.inputFingerprint ?? resumeBinding.pluginAppsInputFingerprint,
2714
- pluginAppPolicyContext: context.prebuiltPluginThreadConfig?.policyContext ?? resumeBinding.pluginAppPolicyContext,
2739
+ pluginAppsFingerprint: pluginThreadConfig?.fingerprint ?? resumeBinding.pluginAppsFingerprint,
2740
+ pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint ?? resumeBinding.pluginAppsInputFingerprint,
2741
+ pluginAppPolicyContext: pluginThreadConfig?.policyContext ?? resumeBinding.pluginAppPolicyContext,
2715
2742
  contextEngine: contextEngineBinding,
2716
2743
  environmentSelectionFingerprint
2717
2744
  };
@@ -2719,7 +2746,7 @@ async function resumeExistingCodexThread(params, context) {
2719
2746
  kind: "patch",
2720
2747
  threadId: resumeBinding.threadId,
2721
2748
  patch: resumePatch
2722
- }))) throw new CodexThreadBindingConflictError(resumeBinding.threadId, "committing a resumed thread");
2749
+ }, context.assertResumeConfiguration))) throw new CodexThreadBindingConflictError(resumeBinding.threadId, "committing a resumed thread");
2723
2750
  if (contextEngineBinding) embeddedAgentLog.info("codex app-server wrote context-engine thread binding", {
2724
2751
  sessionId: params.params.sessionId,
2725
2752
  sessionKey: params.params.sessionKey,
@@ -2756,19 +2783,26 @@ async function resumeExistingCodexThread(params, context) {
2756
2783
  };
2757
2784
  } catch (error) {
2758
2785
  resumeReservation?.release();
2759
- if (isCodexAppServerStartSelectionChangedError(error)) throw error;
2786
+ if (!resumeResponseAccepted && (!(error instanceof CodexAppServerRpcError) || isCodexAppServerOverloadError(error))) throw error;
2760
2787
  if (error instanceof CodexRestrictedToolSurfaceAttestationError) {
2761
- await clearCurrentBinding("retiring a failed restricted-tool-surface attestation");
2788
+ if (!resumeBinding.pendingResumeConfiguration) await clearCurrentBinding("retiring a failed restricted-tool-surface attestation");
2762
2789
  throw error;
2763
2790
  }
2764
- if (error instanceof CodexAdoptedThreadActiveError) throw error;
2765
- if (isCodexAppServerUnsafeSubscriptionError(error)) throw error;
2766
- const resumeRejected = error instanceof CodexAppServerRpcError;
2767
- if (!await unsubscribeCodexThreadBestEffort(params.client, {
2768
- threadId: resumeBinding.threadId,
2769
- timeoutMs: 5e3
2770
- }) && !resumeRejected && !isCodexAppServerConnectionClosedError(error) && !params.signal?.aborted) throw new CodexAppServerUnsafeSubscriptionError("Codex thread/resume subscription cleanup failed", { cause: error });
2771
- if (isCodexAppServerConnectionClosedError(error) || params.signal?.aborted) throw error;
2791
+ if (resumeResponseAccepted) {
2792
+ if (!await unsubscribeCodexThreadBestEffort(params.client, {
2793
+ threadId: resumeBinding.threadId,
2794
+ timeoutMs: 5e3,
2795
+ assertCurrent: context.assertResumeOwnership
2796
+ }).catch(() => false)) {
2797
+ try {
2798
+ await abandonClient();
2799
+ } catch (abandonError) {
2800
+ throw new CodexAppServerUnsafeSubscriptionError("Codex thread/resume client could not be retired", { cause: abandonError });
2801
+ }
2802
+ throw new CodexAppServerUnsafeSubscriptionError("Codex thread/resume subscription cleanup failed", { cause: error });
2803
+ }
2804
+ }
2805
+ if (checkingLoadedPluginThreadConfig || resumeBinding.pendingResumeConfiguration || error instanceof CodexThreadDirectInputError || params.signal?.aborted) throw error;
2772
2806
  embeddedAgentLog.warn("codex app-server thread resume failed; starting a new thread", { error });
2773
2807
  await clearCurrentBinding("rotating a stale thread binding");
2774
2808
  }
@@ -2801,14 +2835,15 @@ async function startFreshCodexThread(params, context) {
2801
2835
  disableLoginShell: params.disableLoginShell
2802
2836
  }));
2803
2837
  const requestModelProvider = typeof startParams.modelProvider === "string" && startParams.modelProvider.trim() ? startParams.modelProvider : void 0;
2804
- const response = assertCodexThreadStartResponse(await lifecycleTiming.measure("thread-start-request", async () => {
2838
+ const threadStartResponse = await lifecycleTiming.measure("thread-start-request", async () => {
2805
2839
  try {
2806
2840
  return await params.client.request("thread/start", startParams, { signal: params.signal });
2807
2841
  } catch (error) {
2808
2842
  if (error instanceof CodexAppServerRpcError) throw new CodexThreadStartRequestError(error);
2809
2843
  throw error;
2810
2844
  }
2811
- }));
2845
+ });
2846
+ const response = assertCodexThreadStartResponse(threadStartResponse);
2812
2847
  const provisionalAppIds = pluginThreadConfig?.provisionalAppIds;
2813
2848
  if (provisionalAppIds?.length) try {
2814
2849
  await lifecycleTiming.measure("plugin-app-attestation", () => attestCodexPluginThreadApps({
@@ -2899,7 +2934,7 @@ async function startFreshCodexThread(params, context) {
2899
2934
  throw new CodexAppServerUnsafeSubscriptionError("Codex successor cleanup failed after a binding replacement conflict", cause === void 0 ? void 0 : { cause });
2900
2935
  }
2901
2936
  };
2902
- const managedSourceHomeId = resolveManagedThreadSourceHomeId(params);
2937
+ const managedSourceHomeId = codexCatalogHomeId(resolveCodexAppServerLocalHomeDir(params.appServer.start, resolveCodexThreadAgentDir(params)));
2903
2938
  await lifecycleTiming.measure("thread-start-mark-managed", () => markStartedCodexManagedThread(params.bindingStore.managedThreads, {
2904
2939
  sourceHomeId: managedSourceHomeId,
2905
2940
  threadId: response.thread.id,
@@ -2958,6 +2993,627 @@ async function startFreshCodexThread(params, context) {
2958
2993
  });
2959
2994
  }
2960
2995
  //#endregion
2996
+ //#region extensions/codex/src/app-server/thread-lifecycle-warm.ts
2997
+ /** Preserves the caller's abort reason across thread ownership transitions. */
2998
+ function throwIfCodexThreadLifecycleAborted(signal) {
2999
+ if (!signal?.aborted) return;
3000
+ const reason = signal.reason;
3001
+ if (reason instanceof Error) throw reason;
3002
+ const error = new Error(typeof reason === "string" && reason.length > 0 ? reason : "codex app-server thread lifecycle aborted");
3003
+ error.name = "AbortError";
3004
+ throw error;
3005
+ }
3006
+ /** Releases consumed subscription ownership or retires an unsafe client. */
3007
+ async function releaseCodexConsumedLiveThread(options) {
3008
+ if (await options.lifecycleTiming.measure("retained-thread-unsubscribe", () => unsubscribeCodexThreadBestEffort(options.client, {
3009
+ threadId: options.threadId,
3010
+ timeoutMs: 5e3,
3011
+ assertCurrent: options.assertCurrent
3012
+ }))) return;
3013
+ return await abandonCodexLiveThreadRelease(options, options.cause);
3014
+ }
3015
+ async function abandonCodexLiveThreadRelease(options, cause) {
3016
+ options.assertCurrent?.();
3017
+ await (options.abandonClient ?? (() => closeCodexStartupClientBestEffort(options.client)))();
3018
+ throw new CodexAppServerUnsafeSubscriptionError(`Codex retained thread subscription could not be released: ${options.threadId}`, cause !== void 0 ? { cause } : void 0);
3019
+ }
3020
+ /** Releases through the retained owner, preserving its guarded callback and rollback. */
3021
+ async function releaseCodexRetainedLiveThread(options) {
3022
+ try {
3023
+ return await options.lifecycleTiming.measure("retained-thread-unsubscribe", () => releaseCodexAppServerLiveThread(options.client, options.threadId, options.assertCurrent));
3024
+ } catch (error) {
3025
+ if (isCodexAppServerUnsafeSubscriptionError(error)) throw error;
3026
+ return await abandonCodexLiveThreadRelease(options, error);
3027
+ }
3028
+ }
3029
+ /** Reuses one safely owned, fully matching subscription on its original client. */
3030
+ async function tryReuseCodexLiveThread(options) {
3031
+ const { params, binding, bindingIdentity, clientId, dynamicToolsFingerprint, environmentSelectionFingerprint, hostSystemAgentActive, lifecycleTiming, nativeSkillIsolation, ringZeroActive, restrictedToolSurfaceInheritedMcpServerNames, startModelProvider, startModelSelection, throwIfAborted, userMcpServersConfigPatch } = options;
3032
+ if (!binding.clientId || binding.clientId !== clientId || binding.preserveNativeModel === true || binding.connectionScope === "supervision" || ringZeroActive) return { kind: "resume" };
3033
+ const retainedThread = await consumeCodexAppServerLiveThread(params.client, binding.threadId);
3034
+ if (!retainedThread) return { kind: "resume" };
3035
+ const assertCurrentClient = () => {
3036
+ throwIfAborted();
3037
+ if (!isCodexAppServerClientRuntimeLive(params.client)) throw params.client.getCloseError() ?? /* @__PURE__ */ new Error("codex app-server client is closed");
3038
+ };
3039
+ let ownershipTransferred = false;
3040
+ try {
3041
+ const pluginThreadConfig = await options.buildLoadedPluginThreadConfig(binding);
3042
+ assertCurrentClient();
3043
+ if (pluginThreadConfig && pluginThreadConfig.fingerprint !== binding.pluginAppsFingerprint) return { kind: "rotate" };
3044
+ const prebuiltFinalConfigPatch = params.buildFinalConfigPatch?.({
3045
+ action: "resume",
3046
+ binding
3047
+ }) ?? {
3048
+ configPatch: params.finalConfigPatch,
3049
+ nativeHookRelayGeneration: params.nativeHookRelayGeneration
3050
+ };
3051
+ const pluginAppsConfigPatch = pluginThreadConfig?.configPatch ?? (params.pluginThreadConfig?.enabled && binding.pluginAppPolicyContext ? buildCodexPluginAppsConfigPatchFromPolicyContext(binding.pluginAppPolicyContext) : void 0);
3052
+ const resumeAuthProfileId = params.params.authProfileId ?? binding.authProfileId;
3053
+ const resumeConfig = mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, prebuiltFinalConfigPatch.configPatch);
3054
+ const resumeParams = lifecycleTiming.measureSync("warm-thread-resume-params", () => buildThreadResumeParams(params.params, {
3055
+ threadId: binding.threadId,
3056
+ cwd: params.cwd,
3057
+ authProfileId: resumeAuthProfileId,
3058
+ model: startModelSelection.model,
3059
+ modelProvider: startModelProvider,
3060
+ preserveNativeModel: false,
3061
+ appServer: params.appServer,
3062
+ dynamicTools: params.dynamicTools,
3063
+ developerInstructions: params.developerInstructions,
3064
+ config: applyCodexNativeSkillIsolation(resumeConfig, nativeSkillIsolation),
3065
+ nativeCodeModeEnabled: params.nativeCodeModeEnabled,
3066
+ nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
3067
+ nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
3068
+ webSearchAllowed: params.webSearchAllowed,
3069
+ hostSystemAgentActive,
3070
+ restrictedToolSurfaceInheritedMcpServerNames,
3071
+ shellEnvironment: params.shellEnvironment,
3072
+ disableLoginShell: params.disableLoginShell
3073
+ }));
3074
+ const liveThreadConfigFingerprint = fingerprintCodexThreadConfig({
3075
+ ...resumeParams,
3076
+ model: binding.model ?? resumeParams.model ?? null,
3077
+ requestedModel: resumeParams.model ?? null,
3078
+ modelProvider: binding.modelProvider ?? resumeParams.modelProvider ?? null,
3079
+ requestedModelProvider: resumeParams.modelProvider ?? binding.modelProvider ?? null
3080
+ }, resumeAuthProfileId, dynamicToolsFingerprint);
3081
+ if (retainedThread.configFingerprint !== liveThreadConfigFingerprint) return {
3082
+ kind: "resume",
3083
+ prebuiltFinalConfigPatch
3084
+ };
3085
+ await attestCodexPluginThreadApps({
3086
+ client: params.client,
3087
+ threadId: binding.threadId,
3088
+ appIds: pluginThreadConfig?.provisionalAppIds ?? [],
3089
+ signal: params.signal
3090
+ });
3091
+ assertCurrentClient();
3092
+ const nativeHookRelayGeneration = prebuiltFinalConfigPatch.nativeHookRelayGeneration ?? binding.nativeHookRelayGeneration;
3093
+ const model = startModelSelection.model;
3094
+ if (!await lifecycleTiming.measure("warm-thread-write-binding", () => params.bindingStore.mutate(bindingIdentity, {
3095
+ kind: "patch",
3096
+ threadId: binding.threadId,
3097
+ patch: {
3098
+ cwd: params.cwd,
3099
+ model,
3100
+ nativeHookRelayGeneration,
3101
+ environmentSelectionFingerprint
3102
+ }
3103
+ }, assertCurrentClient))) throw new CodexThreadBindingConflictError(binding.threadId, "committing a reused thread");
3104
+ assertCurrentClient();
3105
+ lifecycleTiming.mark("thread-ready");
3106
+ lifecycleTiming.logSummary({
3107
+ runId: params.params.runId,
3108
+ sessionId: params.params.sessionId,
3109
+ sessionKey: params.params.sessionKey,
3110
+ threadId: binding.threadId,
3111
+ action: "resumed"
3112
+ });
3113
+ ownershipTransferred = true;
3114
+ return {
3115
+ kind: "ready",
3116
+ binding: {
3117
+ ...binding,
3118
+ cwd: params.cwd,
3119
+ model,
3120
+ nativeHookRelayGeneration,
3121
+ environmentSelectionFingerprint,
3122
+ liveThreadConfigFingerprint,
3123
+ liveThreadOwnership: retainedThread,
3124
+ ...retainedThread.serviceTier && resumeParams.serviceTier === void 0 ? { clearInheritedServiceTier: true } : {},
3125
+ lifecycle: { action: "resumed" }
3126
+ }
3127
+ };
3128
+ } finally {
3129
+ if (!ownershipTransferred) try {
3130
+ await retainedThread.release(binding.threadId);
3131
+ } catch (error) {
3132
+ await abandonCodexLiveThreadRelease({
3133
+ client: params.client,
3134
+ abandonClient: params.abandonClient,
3135
+ lifecycleTiming,
3136
+ threadId: binding.threadId
3137
+ }, error);
3138
+ }
3139
+ }
3140
+ }
3141
+ //#endregion
3142
+ //#region extensions/codex/src/app-server/thread-lifecycle-adoption.ts
3143
+ /** Passive refusal must precede releasing or acquiring any native subscription. */
3144
+ async function assertAdoptedCodexThreadResumeAllowed(params, threadId, context) {
3145
+ const { thread } = await context.lifecycleTiming.measure("thread-read-adoption-status", () => params.client.request("thread/read", {
3146
+ threadId,
3147
+ includeTurns: false
3148
+ }, { signal: params.signal }));
3149
+ context.throwIfAborted();
3150
+ assertCodexThreadAcceptsDirectInput(thread);
3151
+ if (thread.status?.type === "active") throw new CodexAdoptedThreadActiveError();
3152
+ }
3153
+ /** Preserve attach's native-queue-before-binding-lease order when consuming pending intent. */
3154
+ async function withCodexThreadLifecycleBinding(params, run) {
3155
+ const identity = sessionBindingIdentity({
3156
+ sessionId: params.params.sessionId,
3157
+ sessionKey: params.params.sessionKey,
3158
+ agentId: params.agentId ?? params.params.agentId,
3159
+ config: params.params.config
3160
+ });
3161
+ const snapshot = await params.bindingStore.read(identity);
3162
+ const pendingThreadId = snapshot?.pendingResumeConfiguration ? snapshot.threadId : void 0;
3163
+ const runWithLease = () => params.bindingStore.withLease(identity, async () => {
3164
+ const binding = await params.bindingStore.read(identity);
3165
+ if (pendingThreadId && (binding?.threadId !== pendingThreadId || !binding.pendingResumeConfiguration)) throw new CodexThreadBindingConflictError(pendingThreadId, "acquiring a pending resume configuration");
3166
+ if (!pendingThreadId && binding?.pendingResumeConfiguration) throw new CodexThreadBindingConflictError(binding.threadId, "acquiring a pending resume configuration");
3167
+ return await run(identity, binding);
3168
+ });
3169
+ return pendingThreadId ? await withExclusiveCodexAppServerThread({
3170
+ bindingStore: params.bindingStore,
3171
+ identity,
3172
+ threadId: pendingThreadId,
3173
+ run: runWithLease
3174
+ }) : await runWithLease();
3175
+ }
3176
+ /** Completes manual attachment only under the native queue and exact binding lease. */
3177
+ async function resumePendingCodexThread(params, context) {
3178
+ const { binding, contextEngineBinding, lifecycleTiming, restrictedToolSurface } = context;
3179
+ if (isIncognitoSessionKey(params.params.sessionKey) || context.transientRestriction || !restrictedToolSurface && binding.nativeToolPolicyRestricted === true || (contextEngineBinding ? !isContextEngineBindingCompatible(binding.contextEngine, contextEngineBinding) : binding.contextEngine !== void 0) || shouldRotateCodexGpt56MultiAgentBinding({
3180
+ bindingModel: binding.model,
3181
+ requestedModel: params.params.modelId
3182
+ })) throw new Error(`Cannot configure resumed Codex thread ${binding.threadId} under a transient or incompatible session policy. The thread is preserved; retry from its normal session or use /new for the current policy.`);
3183
+ const prebuiltPluginThreadConfig = params.pluginThreadConfig?.enabled ? await lifecycleTiming.measure("plugin-config-build", () => params.pluginThreadConfig?.build()) : void 0;
3184
+ const clientId = resolveCodexAppServerClientInstanceId(params.client);
3185
+ const configuration = await preparePendingCodexThreadResume(params, binding, context.dynamicToolsFingerprint, async (assertCurrent) => {
3186
+ const released = await context.releaseRetainedThread(binding.threadId, assertCurrent);
3187
+ assertCurrent();
3188
+ if (!released || binding.clientId && binding.clientId !== clientId) await releaseCodexConsumedLiveThread({
3189
+ client: params.client,
3190
+ abandonClient: params.abandonClient,
3191
+ lifecycleTiming,
3192
+ threadId: binding.threadId,
3193
+ assertCurrent
3194
+ });
3195
+ });
3196
+ try {
3197
+ const resumed = await resumeExistingCodexThread(params, {
3198
+ ...context,
3199
+ prebuiltPluginThreadConfig,
3200
+ assertResumeConfiguration: configuration.assertConfigured,
3201
+ assertResumeOwnership: configuration.assertCurrent
3202
+ });
3203
+ if (!resumed) throw new Error(`Codex did not configure resumed thread ${binding.threadId}.`);
3204
+ return resumed;
3205
+ } finally {
3206
+ configuration.dispose();
3207
+ }
3208
+ }
3209
+ /** Manual attachment is intent, never evidence that loaded native overrides took effect. */
3210
+ async function preparePendingCodexThreadResume(params, binding, dynamicToolsFingerprint, releaseSubscription) {
3211
+ const fail = (reason) => /* @__PURE__ */ new Error(`Cannot configure resumed Codex thread ${binding.threadId}: ${reason}. The thread is preserved; continue it in native Codex or use /new for the current OpenClaw tools.`);
3212
+ const agentDir = resolveCodexThreadAgentDir(params);
3213
+ const localHome = resolveCodexAppServerLocalHomeDir(params.appServer.start, agentDir);
3214
+ if (params.appServer.start.transport !== "stdio" || params.appServer.start.homeScope === "user" || path.resolve(localHome) !== resolveCodexAppServerHomeDir(agentDir) || binding.connectionScope === "supervision" || binding.preserveNativeModel === true) throw fail("configuration adoption requires an OpenClaw-owned local Codex home");
3215
+ if (isCodexAppServerLiveThreadClaimed(params.client, binding.threadId)) throw fail("the thread is claimed by active work; stop that run before resuming");
3216
+ const assertCurrent = captureExclusiveSharedCodexAppServerClient(params.client);
3217
+ const { thread } = await params.client.request("thread/read", {
3218
+ threadId: binding.threadId,
3219
+ includeTurns: false
3220
+ }, { signal: params.signal });
3221
+ assertCurrent();
3222
+ const statusType = thread.status?.type;
3223
+ if (thread.id !== binding.threadId || statusType !== "idle" && statusType !== "notLoaded") throw fail("the native thread is not idle; wait for its current run to finish");
3224
+ assertCodexThreadAcceptsDirectInput(thread);
3225
+ let unloaded = statusType === "notLoaded";
3226
+ const dispose = params.client.addNotificationHandler((notification) => {
3227
+ if (notification.method === "thread/status/changed" && isJsonObject(notification.params) && notification.params.threadId === binding.threadId && isJsonObject(notification.params.status) && notification.params.status.type === "notLoaded") unloaded = true;
3228
+ });
3229
+ try {
3230
+ const rolloutPath = thread.path ?? binding.rolloutPath;
3231
+ const metadata = rolloutPath ? await readCodexSessionMeta(path.join(localHome, "sessions"), rolloutPath, binding.threadId) : void 0;
3232
+ if (!metadata) throw fail("its native tool catalog could not be read from the selected Codex home");
3233
+ const recordedTools = metadata.dynamic_tools ?? [];
3234
+ if (!Array.isArray(recordedTools) || codexDynamicToolsFingerprint(recordedTools) !== dynamicToolsFingerprint) throw fail("its immutable native tool catalog does not match the current OpenClaw tools");
3235
+ assertCurrent();
3236
+ await releaseSubscription(assertCurrent);
3237
+ assertCurrent();
3238
+ return {
3239
+ assertConfigured: () => {
3240
+ assertCurrent();
3241
+ if (!unloaded) throw fail("Codex did not confirm unloading its previous configuration");
3242
+ },
3243
+ assertCurrent,
3244
+ dispose
3245
+ };
3246
+ } catch (error) {
3247
+ dispose();
3248
+ throw error;
3249
+ }
3250
+ }
3251
+ //#endregion
3252
+ //#region extensions/codex/src/app-server/native-hook-relay-state.ts
3253
+ const pending = /* @__PURE__ */ new Set();
3254
+ /** Owns delayed hook-relay cleanup across runtime scheduling and test teardown. */
3255
+ const nativeHookRelayUnregisterQueue = {
3256
+ add(entry) {
3257
+ pending.add(entry);
3258
+ },
3259
+ delete(entry) {
3260
+ return pending.delete(entry);
3261
+ },
3262
+ flush() {
3263
+ while (pending.size > 0) {
3264
+ const entry = pending.values().next().value;
3265
+ if (!entry) return;
3266
+ clearTimeout(entry.timeout);
3267
+ entry.unregister();
3268
+ }
3269
+ },
3270
+ clear() {
3271
+ for (const entry of pending) clearTimeout(entry.timeout);
3272
+ pending.clear();
3273
+ }
3274
+ };
3275
+ //#endregion
3276
+ //#region extensions/codex/src/app-server/native-hook-relay.ts
3277
+ /**
3278
+ * Bridges Codex native hook callbacks into OpenClaw's native hook relay so
3279
+ * app-server tool events can still run OpenClaw policy and diagnostics.
3280
+ */
3281
+ /** Codex hook events that can be registered through OpenClaw's native relay. */
3282
+ const CODEX_NATIVE_HOOK_RELAY_EVENTS = [
3283
+ "pre_tool_use",
3284
+ "post_tool_use",
3285
+ "permission_request",
3286
+ "before_agent_finalize"
3287
+ ];
3288
+ const CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS = CODEX_NATIVE_HOOK_RELAY_EVENTS.filter((event) => event !== "permission_request");
3289
+ const CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS = 18e5;
3290
+ /** Extra relay lifetime after the expected turn budget, preventing late hook drops. */
3291
+ const CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS = 3e5;
3292
+ const CODEX_NATIVE_HOOK_RELAY_COMMAND_MIN_PARENT_MARGIN_MS = 250;
3293
+ const CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS = 1e3;
3294
+ const CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC = 10;
3295
+ const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS = 1e4;
3296
+ const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS = 5e3;
3297
+ const MAX_PENDING_DIRECT_CHILD_ADMISSIONS = 32;
3298
+ const nativeHookPolicyByClient = /* @__PURE__ */ new WeakMap();
3299
+ const CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID = {
3300
+ exec: [
3301
+ "Bash",
3302
+ "exec",
3303
+ "exec_command"
3304
+ ],
3305
+ apply_patch: [
3306
+ "apply_patch",
3307
+ "Write",
3308
+ "Edit"
3309
+ ],
3310
+ spawn_agent: ["spawn_agent", "Agent"]
3311
+ };
3312
+ /** Enterprise managed-only policy silently drops the session-layer hooks that enforce OpenClaw. */
3313
+ async function assertCodexNativeHookRelayAllowed(client, signal) {
3314
+ let attestation = nativeHookPolicyByClient.get(client);
3315
+ if (!attestation) {
3316
+ attestation = client.request("configRequirements/read", void 0, { signal }).then((response) => {
3317
+ if (!isJsonObject(response) || !Object.hasOwn(response, "requirements")) throw new Error("Codex configRequirements/read returned an invalid hook policy response");
3318
+ const requirements = response.requirements;
3319
+ if (requirements === null) return;
3320
+ if (!isJsonObject(requirements)) throw new Error("Codex configRequirements/read returned invalid hook policy requirements");
3321
+ const managedOnly = requirements.allowManagedHooksOnly;
3322
+ if (managedOnly !== void 0 && managedOnly !== null && typeof managedOnly !== "boolean") throw new Error("Codex configRequirements/read returned invalid managed-only hook policy");
3323
+ if (managedOnly === true) throw new Error("Codex managed-only hooks disable the OpenClaw native hook relay; refusing unenforced execution");
3324
+ });
3325
+ nativeHookPolicyByClient.set(client, attestation);
3326
+ attestation.catch(() => {
3327
+ if (nativeHookPolicyByClient.get(client) === attestation) nativeHookPolicyByClient.delete(client);
3328
+ });
3329
+ }
3330
+ await attestation;
3331
+ }
3332
+ /** Defers relay unregister so late native hook subprocesses can still resolve. */
3333
+ function scheduleCodexNativeHookRelayUnregister(params) {
3334
+ let pending;
3335
+ const unregister = () => {
3336
+ if (!pending) return;
3337
+ const current = pending;
3338
+ pending = void 0;
3339
+ if (!nativeHookRelayUnregisterQueue.delete(current)) return;
3340
+ params.relay.unregister();
3341
+ };
3342
+ const timeout = setTimeout(unregister, resolveCodexNativeHookRelayUnregisterGraceMs(params.hookTimeoutSec));
3343
+ pending = {
3344
+ timeout,
3345
+ unregister
3346
+ };
3347
+ nativeHookRelayUnregisterQueue.add(pending);
3348
+ timeout.unref();
3349
+ }
3350
+ /** Computes the delayed unregister window from Codex's hook timeout. */
3351
+ function resolveCodexNativeHookRelayUnregisterGraceMs(hookTimeoutSec) {
3352
+ const hookTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 0;
3353
+ return Math.max(CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS, addTimerTimeoutGraceMs(hookTimeoutMs, CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS) ?? 0);
3354
+ }
3355
+ /** Records a native pre-tool failure that Codex does not project as a tool item. */
3356
+ function emitCodexNativePreToolUseFailureDiagnostic(params) {
3357
+ emitTrustedDiagnosticEvent({
3358
+ type: "tool.execution.error",
3359
+ ...params.agentId ? { agentId: params.agentId } : {},
3360
+ sessionId: params.sessionId,
3361
+ ...params.sessionKey ? { sessionKey: params.sessionKey } : {},
3362
+ runId: params.runId,
3363
+ toolName: params.failure.toolName,
3364
+ toolCallId: params.failure.toolCallId,
3365
+ durationMs: params.failure.durationMs,
3366
+ errorCategory: "before_tool_call",
3367
+ terminalReason: params.terminalReason ?? (params.signal?.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : params.failure.disposition),
3368
+ ...params.sourceTimestampMs !== void 0 ? { sourceTimestampMs: params.sourceTimestampMs } : {}
3369
+ });
3370
+ }
3371
+ /** Registers an OpenClaw native hook relay for a Codex app-server turn. */
3372
+ function createCodexNativeHookRelay(params) {
3373
+ if (params.options?.enabled === false) return;
3374
+ const directChildClaims = /* @__PURE__ */ new Map();
3375
+ const pendingDirectChildAdmissions = /* @__PURE__ */ new Map();
3376
+ let foregroundClosed = false;
3377
+ let successfulYieldRetentionAuthorized = false;
3378
+ const assertClaim = (threadId, claim) => () => directChildClaims.get(threadId) === claim;
3379
+ const rejectPendingAdmissions = (reason) => {
3380
+ for (const pending of pendingDirectChildAdmissions.values()) pending.reject(new Error(reason));
3381
+ pendingDirectChildAdmissions.clear();
3382
+ };
3383
+ const relay = registerRetainedNativeHookRelayForBundledRuntime({
3384
+ provider: "codex",
3385
+ relayId: buildCodexNativeHookRelayId({
3386
+ agentId: params.agentId,
3387
+ sessionId: params.sessionId,
3388
+ sessionKey: params.sessionKey
3389
+ }),
3390
+ ...params.generation ? { generation: params.generation } : {},
3391
+ ...params.generationMismatchGraceMs ? { generationMismatchGraceMs: params.generationMismatchGraceMs } : {},
3392
+ ...params.agentId ? { agentId: params.agentId } : {},
3393
+ sessionId: params.sessionId,
3394
+ ...params.sessionKey ? { sessionKey: params.sessionKey } : {},
3395
+ ...params.config ? { config: params.config } : {},
3396
+ runId: params.runId,
3397
+ ...params.channelId ? { channelId: params.channelId } : {},
3398
+ ...params.requester ? { requester: params.requester } : {},
3399
+ ...params.approvalContext ? { approvalContext: params.approvalContext } : {},
3400
+ allowedEvents: params.events,
3401
+ preToolUseLoopDetection: params.loopDetectionPreToolUseRelay,
3402
+ ttlMs: resolveCodexNativeHookRelayTtlMs({
3403
+ explicitTtlMs: params.options?.ttlMs,
3404
+ attemptTimeoutMs: params.attemptTimeoutMs,
3405
+ startupTimeoutMs: params.startupTimeoutMs,
3406
+ turnStartTimeoutMs: params.turnStartTimeoutMs
3407
+ }),
3408
+ signal: params.signal,
3409
+ runBeforeToolCall: params.hostCapabilities.runBeforeToolCall,
3410
+ assertActive: params.hostCapabilities.assertActive,
3411
+ retention: {
3412
+ readClaim: readCodexNativeChildThreadId,
3413
+ shouldRetainAfterForegroundClose: () => successfulYieldRetentionAuthorized && directChildClaims.size > 0,
3414
+ allowPreToolUse: (childThreadId) => directChildClaims.has(childThreadId),
3415
+ awaitForegroundAdmission: (childThreadId) => {
3416
+ if (foregroundClosed) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission unavailable"));
3417
+ const existingClaim = directChildClaims.get(childThreadId);
3418
+ if (existingClaim) return Promise.resolve(assertClaim(childThreadId, existingClaim));
3419
+ const existingPending = pendingDirectChildAdmissions.get(childThreadId);
3420
+ if (existingPending) return existingPending.promise.then((claim) => assertClaim(childThreadId, claim));
3421
+ if (pendingDirectChildAdmissions.size >= MAX_PENDING_DIRECT_CHILD_ADMISSIONS) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission capacity reached"));
3422
+ const { promise, resolve, reject } = createDeferred();
3423
+ pendingDirectChildAdmissions.set(childThreadId, {
3424
+ promise,
3425
+ resolve,
3426
+ reject
3427
+ });
3428
+ return promise.then((claim) => assertClaim(childThreadId, claim));
3429
+ },
3430
+ onDispose: () => {
3431
+ foregroundClosed = true;
3432
+ rejectPendingAdmissions("native hook relay registration closed");
3433
+ }
3434
+ },
3435
+ onPreToolUseFailure: params.onPreToolUseFailure,
3436
+ command: {
3437
+ nice: 10,
3438
+ timeoutMs: params.options?.gatewayTimeoutMs
3439
+ }
3440
+ });
3441
+ const unregister = () => {
3442
+ foregroundClosed = true;
3443
+ rejectPendingAdmissions("native hook relay foreground closed");
3444
+ relay.unregister();
3445
+ };
3446
+ return {
3447
+ ...relay,
3448
+ unregister,
3449
+ authorizeRetentionAfterSuccessfulYield: () => {
3450
+ successfulYieldRetentionAuthorized = true;
3451
+ },
3452
+ hasClaimedDirectChild: () => directChildClaims.size > 0,
3453
+ rejectPendingDirectChild: (threadIdInput, reason) => {
3454
+ const threadId = threadIdInput.trim();
3455
+ const pending = threadId ? pendingDirectChildAdmissions.get(threadId) : void 0;
3456
+ if (!pending) return;
3457
+ pendingDirectChildAdmissions.delete(threadId);
3458
+ pending.reject(new Error(reason));
3459
+ },
3460
+ claimDirectChild: (threadIdInput) => {
3461
+ const threadId = threadIdInput.trim();
3462
+ if (!threadId) return () => void 0;
3463
+ if (directChildClaims.get(threadId)) return () => void 0;
3464
+ const claim = Symbol(threadId);
3465
+ directChildClaims.set(threadId, claim);
3466
+ const pending = pendingDirectChildAdmissions.get(threadId);
3467
+ pendingDirectChildAdmissions.delete(threadId);
3468
+ pending?.resolve(claim);
3469
+ let released = false;
3470
+ return () => {
3471
+ if (released) return;
3472
+ released = true;
3473
+ if (directChildClaims.get(threadId) !== claim) return;
3474
+ directChildClaims.delete(threadId);
3475
+ if (foregroundClosed && directChildClaims.size === 0) relay.unregister();
3476
+ };
3477
+ }
3478
+ };
3479
+ }
3480
+ function readCodexNativeChildThreadId(rawPayload) {
3481
+ if (!isJsonObject(rawPayload) || typeof rawPayload.agent_id !== "string") return;
3482
+ return rawPayload.agent_id.trim() || void 0;
3483
+ }
3484
+ /** Selects the native hook events Codex should install for the current approval mode. */
3485
+ function resolveCodexNativeHookRelayEvents(params) {
3486
+ if (params.configuredEvents?.length) return params.configuredEvents;
3487
+ return params.appServer.approvalPolicy === "never" ? CODEX_NATIVE_HOOK_RELAY_EVENTS : CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS;
3488
+ }
3489
+ /** Derives the native hook relay TTL from the turn budget unless explicitly configured. */
3490
+ function resolveCodexNativeHookRelayTtlMs(params) {
3491
+ if (params.explicitTtlMs !== void 0) return params.explicitTtlMs;
3492
+ const relayBudgetMs = params.attemptTimeoutMs + params.startupTimeoutMs + params.turnStartTimeoutMs + CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS;
3493
+ return Math.max(CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS, Math.floor(relayBudgetMs));
3494
+ }
3495
+ /** Builds a stable relay id scoped to the agent and session identity. */
3496
+ function buildCodexNativeHookRelayId(params) {
3497
+ const hash = createHash("sha256");
3498
+ hash.update("openclaw:codex:native-hook-relay:v1");
3499
+ hash.update("\0");
3500
+ hash.update(params.agentId?.trim() || "");
3501
+ hash.update("\0");
3502
+ hash.update(params.sessionKey?.trim() || params.sessionId);
3503
+ return `codex-${hash.digest("hex").slice(0, 40)}`;
3504
+ }
3505
+ const CODEX_HOOK_EVENT_BY_NATIVE_EVENT = {
3506
+ pre_tool_use: "PreToolUse",
3507
+ post_tool_use: "PostToolUse",
3508
+ permission_request: "PermissionRequest",
3509
+ before_agent_finalize: "Stop"
3510
+ };
3511
+ const CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT = {
3512
+ pre_tool_use: "pre_tool_use",
3513
+ post_tool_use: "post_tool_use",
3514
+ permission_request: "permission_request",
3515
+ before_agent_finalize: "stop"
3516
+ };
3517
+ const CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS = ["/<session-flags>/config.toml", "<session-flags>/config.toml"];
3518
+ /** Builds the Codex config overlay that installs trusted command hooks for relay events. */
3519
+ function buildCodexNativeHookRelayConfig(params) {
3520
+ const events = params.events?.length ? params.events : CODEX_NATIVE_HOOK_RELAY_EVENTS;
3521
+ const selectedEvents = new Set(events);
3522
+ const config = { "features.hooks": true };
3523
+ const hookState = {};
3524
+ for (const event of CODEX_NATIVE_HOOK_RELAY_EVENTS) {
3525
+ const codexEvent = CODEX_HOOK_EVENT_BY_NATIVE_EVENT[event];
3526
+ const selected = selectedEvents.has(event);
3527
+ const shouldRelay = params.relay.shouldRelayEvent(event);
3528
+ if (!selected || !shouldRelay) {
3529
+ if (selected || params.clearOmittedEvents) config[`hooks.${codexEvent}`] = [];
3530
+ if (params.clearOmittedEvents) for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = { enabled: false };
3531
+ continue;
3532
+ }
3533
+ const timeout = normalizeHookTimeoutSec(params.hookTimeoutSec);
3534
+ const command = params.relay.commandForEvent(event, { timeoutMs: resolveCodexNativeHookRelayCommandTimeoutMs(timeout) });
3535
+ const matcher = buildCodexNativeToolMatcher(params.relay.toolMatcherForEvent(event));
3536
+ config[`hooks.${codexEvent}`] = [{
3537
+ ...matcher ? { matcher } : {},
3538
+ hooks: [{
3539
+ type: "command",
3540
+ command,
3541
+ timeout,
3542
+ async: false,
3543
+ statusMessage: "OpenClaw native hook relay"
3544
+ }]
3545
+ }];
3546
+ const state = {
3547
+ enabled: true,
3548
+ trusted_hash: codexCommandHookTrustedHash({
3549
+ event,
3550
+ command,
3551
+ matcher,
3552
+ timeout,
3553
+ statusMessage: "OpenClaw native hook relay"
3554
+ })
3555
+ };
3556
+ for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = state;
3557
+ }
3558
+ config["hooks.state"] = hookState;
3559
+ return config;
3560
+ }
3561
+ /** Builds a Codex config overlay that disables native hooks and clears hook arrays. */
3562
+ function buildCodexNativeHookRelayDisabledConfig() {
3563
+ return {
3564
+ "features.hooks": false,
3565
+ "hooks.PreToolUse": [],
3566
+ "hooks.PostToolUse": [],
3567
+ "hooks.PermissionRequest": [],
3568
+ "hooks.Stop": []
3569
+ };
3570
+ }
3571
+ function normalizeHookTimeoutSec(value) {
3572
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.ceil(value) : CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC;
3573
+ }
3574
+ function resolveCodexNativeHookRelayCommandTimeoutMs(hookTimeoutSec) {
3575
+ const parentTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 5e3;
3576
+ const parentMarginMs = Math.min(CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS, Math.max(CODEX_NATIVE_HOOK_RELAY_COMMAND_MIN_PARENT_MARGIN_MS, Math.floor(parentTimeoutMs / 5)));
3577
+ return Math.max(1, parentTimeoutMs - parentMarginMs);
3578
+ }
3579
+ function buildCodexNativeToolMatcher(toolNames) {
3580
+ if (toolNames === void 0) return;
3581
+ if (toolNames.length === 0) throw new TypeError("Codex native hook matcher requires at least one tool name");
3582
+ const nativeNames = /* @__PURE__ */ new Set();
3583
+ let hasCustomToolName = false;
3584
+ for (const toolName of toolNames) {
3585
+ const canonicalToolName = toolName.trim();
3586
+ if (!canonicalToolName || canonicalToolName === "*") throw new TypeError("Codex native hook matcher requires canonical OpenClaw tool ids");
3587
+ const nativeAliases = CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID[canonicalToolName];
3588
+ if (!nativeAliases) hasCustomToolName = true;
3589
+ for (const nativeName of nativeAliases ?? [canonicalToolName]) nativeNames.add(nativeName);
3590
+ }
3591
+ const sortedNames = Array.from(nativeNames).toSorted();
3592
+ if (!hasCustomToolName && sortedNames.every((toolName) => /^[A-Za-z0-9_]+$/.test(toolName))) return sortedNames.join("|");
3593
+ return `(?i)^(?:${sortedNames.map((toolName) => toolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})$`;
3594
+ }
3595
+ function codexCommandHookTrustedHash(params) {
3596
+ const identity = {
3597
+ event_name: CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[params.event],
3598
+ ...params.matcher ? { matcher: params.matcher } : {},
3599
+ hooks: [{
3600
+ async: false,
3601
+ command: params.command,
3602
+ statusMessage: params.statusMessage,
3603
+ timeout: params.timeout,
3604
+ type: "command"
3605
+ }]
3606
+ };
3607
+ return `sha256:${createHash("sha256").update(JSON.stringify(sortJsonValue(identity))).digest("hex")}`;
3608
+ }
3609
+ function sortJsonValue(value) {
3610
+ if (!value || typeof value !== "object") return value;
3611
+ if (Array.isArray(value)) return value.map(sortJsonValue);
3612
+ const sorted = {};
3613
+ for (const [key, entry] of Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right))) sorted[key] = sortJsonValue(entry);
3614
+ return sorted;
3615
+ }
3616
+ //#endregion
2961
3617
  //#region extensions/codex/src/app-server/profiler-flag.ts
2962
3618
  const PROFILER_FLAGS = ["profiler", "codex.profiler"];
2963
3619
  /** Checks the generic and Codex-specific profiler diagnostic flags. */
@@ -3056,6 +3712,13 @@ function createCodexThreadLifecycleTimingTracker(options = {}) {
3056
3712
  //#endregion
3057
3713
  //#region extensions/codex/src/app-server/thread-lifecycle-preflight.ts
3058
3714
  async function prepareCodexThreadLifecyclePreflight(params) {
3715
+ await assertCodexModelBackedReviewerEffectiveConfig({
3716
+ client: params.client,
3717
+ approvalsReviewer: params.appServer.approvalsReviewer,
3718
+ cwd: params.cwd,
3719
+ signal: params.signal
3720
+ });
3721
+ if (params.nativeHookRelayRequired) await assertCodexNativeHookRelayAllowed(params.client, params.signal);
3059
3722
  const lifecycleTiming = createCodexThreadLifecycleTimingTracker({
3060
3723
  ...params.timing,
3061
3724
  enabled: params.timing?.enabled ?? isCodexAppServerProfilerEnabled(params.params.config)
@@ -3072,11 +3735,12 @@ async function prepareCodexThreadLifecyclePreflight(params) {
3072
3735
  })).threadConfig);
3073
3736
  const networkProxyConfigFingerprint = params.appServer.networkProxy?.configFingerprint;
3074
3737
  const contextEngineBinding = lifecycleTiming.measureSync("context-engine-binding", () => buildContextEngineBinding(params.params, params.contextEngineProjection));
3075
- const userMcpServersConfigPatch = params.userMcpServersEnabled === false ? void 0 : await buildCodexUserMcpServersThreadConfigPatchForRuntime(params.params.config, {
3738
+ const userMcpServersConfigPatch = params.userMcpServersEnabled === false ? void 0 : await buildCodexUserMcpServersThreadConfigPatchForRun({
3739
+ run: params.params,
3740
+ cwd: params.cwd,
3076
3741
  agentId: params.agentId ?? params.params.agentId,
3077
- agentDir: params.params.agentDir,
3078
3742
  allowLiteralOAuthProjection: params.appServer.connectionClass !== "remote",
3079
- toolOverrides: params.params.toolOverrides,
3743
+ warn: (message) => embeddedAgentLog.warn(message),
3080
3744
  onServerUnavailable: (serverName, error) => embeddedAgentLog.warn("skipping unavailable MCP OAuth server", {
3081
3745
  serverName,
3082
3746
  error: formatErrorMessage(error)
@@ -3136,118 +3800,6 @@ async function prepareCodexThreadLifecyclePreflight(params) {
3136
3800
  };
3137
3801
  }
3138
3802
  //#endregion
3139
- //#region extensions/codex/src/app-server/thread-lifecycle-warm.ts
3140
- /** Preserves the caller's abort reason across thread ownership transitions. */
3141
- function throwIfCodexThreadLifecycleAborted(signal) {
3142
- if (!signal?.aborted) return;
3143
- const reason = signal.reason;
3144
- if (reason instanceof Error) throw reason;
3145
- const error = new Error(typeof reason === "string" && reason.length > 0 ? reason : "codex app-server thread lifecycle aborted");
3146
- error.name = "AbortError";
3147
- throw error;
3148
- }
3149
- /** Releases consumed subscription ownership or retires an unsafe client. */
3150
- async function releaseCodexConsumedLiveThread(options) {
3151
- if (await options.lifecycleTiming.measure("retained-thread-unsubscribe", () => unsubscribeCodexThreadBestEffort(options.client, {
3152
- threadId: options.threadId,
3153
- timeoutMs: 5e3
3154
- }))) return;
3155
- await (options.abandonClient ?? (() => closeCodexStartupClientBestEffort(options.client)))();
3156
- throw new CodexAppServerUnsafeSubscriptionError(`Codex retained thread subscription could not be released: ${options.threadId}`, options.cause !== void 0 ? { cause: options.cause } : void 0);
3157
- }
3158
- /** Transfers retained-slot ownership before unconditionally releasing it. */
3159
- async function releaseCodexRetainedLiveThread(options) {
3160
- if (await consumeCodexAppServerLiveThread(options.client, options.threadId)) await releaseCodexConsumedLiveThread(options);
3161
- }
3162
- /** Reuses one safely owned, fully matching subscription on its original client. */
3163
- async function tryReuseCodexLiveThread(options) {
3164
- const { params, binding, bindingIdentity, clientId, dynamicToolsFingerprint, environmentSelectionFingerprint, hostSystemAgentActive, lifecycleTiming, nativeSkillIsolation, releaseConsumedThread, ringZeroActive, restrictedToolSurfaceInheritedMcpServerNames, startModelProvider, startModelSelection, throwIfAborted, userMcpServersConfigPatch } = options;
3165
- if (!binding.clientId || binding.clientId !== clientId || binding.preserveNativeModel === true || binding.connectionScope === "supervision" || ringZeroActive) return {};
3166
- const prebuiltFinalConfigPatch = params.buildFinalConfigPatch?.({
3167
- action: "resume",
3168
- binding
3169
- }) ?? {
3170
- configPatch: params.finalConfigPatch,
3171
- nativeHookRelayGeneration: params.nativeHookRelayGeneration
3172
- };
3173
- const pluginAppsConfigPatch = params.pluginThreadConfig?.enabled && binding.pluginAppPolicyContext ? buildCodexPluginAppsConfigPatchFromPolicyContext(binding.pluginAppPolicyContext) : void 0;
3174
- const resumeAuthProfileId = params.params.authProfileId ?? binding.authProfileId;
3175
- const resumeConfig = mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, prebuiltFinalConfigPatch.configPatch);
3176
- const resumeParams = lifecycleTiming.measureSync("warm-thread-resume-params", () => buildThreadResumeParams(params.params, {
3177
- threadId: binding.threadId,
3178
- cwd: params.cwd,
3179
- authProfileId: resumeAuthProfileId,
3180
- model: startModelSelection.model,
3181
- modelProvider: startModelProvider,
3182
- preserveNativeModel: false,
3183
- appServer: params.appServer,
3184
- dynamicTools: params.dynamicTools,
3185
- developerInstructions: params.developerInstructions,
3186
- config: applyCodexNativeSkillIsolation(resumeConfig, nativeSkillIsolation),
3187
- nativeCodeModeEnabled: params.nativeCodeModeEnabled,
3188
- nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
3189
- nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
3190
- webSearchAllowed: params.webSearchAllowed,
3191
- hostSystemAgentActive,
3192
- restrictedToolSurfaceInheritedMcpServerNames,
3193
- shellEnvironment: params.shellEnvironment,
3194
- disableLoginShell: params.disableLoginShell
3195
- }));
3196
- const liveThreadConfigFingerprint = fingerprintCodexThreadConfig({
3197
- ...resumeParams,
3198
- model: binding.model ?? resumeParams.model ?? null,
3199
- requestedModel: resumeParams.model ?? null,
3200
- modelProvider: binding.modelProvider ?? resumeParams.modelProvider ?? null,
3201
- requestedModelProvider: resumeParams.modelProvider ?? binding.modelProvider ?? null
3202
- }, resumeAuthProfileId, dynamicToolsFingerprint);
3203
- const retainedThread = await consumeCodexAppServerLiveThread(params.client, binding.threadId, liveThreadConfigFingerprint);
3204
- if (!retainedThread) {
3205
- const incompatibleOwnership = await consumeCodexAppServerLiveThread(params.client, binding.threadId);
3206
- if (incompatibleOwnership) await incompatibleOwnership.release(binding.threadId);
3207
- return { prebuiltFinalConfigPatch };
3208
- }
3209
- try {
3210
- const nativeHookRelayGeneration = prebuiltFinalConfigPatch.nativeHookRelayGeneration ?? binding.nativeHookRelayGeneration;
3211
- const model = startModelSelection.model;
3212
- if (!await lifecycleTiming.measure("warm-thread-write-binding", () => params.bindingStore.mutate(bindingIdentity, {
3213
- kind: "patch",
3214
- threadId: binding.threadId,
3215
- patch: {
3216
- cwd: params.cwd,
3217
- model,
3218
- nativeHookRelayGeneration,
3219
- environmentSelectionFingerprint
3220
- }
3221
- }))) throw new CodexThreadBindingConflictError(binding.threadId, "committing a reused thread");
3222
- throwIfAborted();
3223
- lifecycleTiming.mark("thread-ready");
3224
- lifecycleTiming.logSummary({
3225
- runId: params.params.runId,
3226
- sessionId: params.params.sessionId,
3227
- sessionKey: params.params.sessionKey,
3228
- threadId: binding.threadId,
3229
- action: "resumed"
3230
- });
3231
- return {
3232
- binding: {
3233
- ...binding,
3234
- cwd: params.cwd,
3235
- model,
3236
- nativeHookRelayGeneration,
3237
- environmentSelectionFingerprint,
3238
- liveThreadConfigFingerprint,
3239
- liveThreadOwnership: retainedThread,
3240
- ...retainedThread.serviceTier && resumeParams.serviceTier === void 0 ? { clearInheritedServiceTier: true } : {},
3241
- lifecycle: { action: "resumed" }
3242
- },
3243
- prebuiltFinalConfigPatch
3244
- };
3245
- } catch (error) {
3246
- await releaseConsumedThread(binding.threadId, error);
3247
- throw error;
3248
- }
3249
- }
3250
- //#endregion
3251
3803
  //#region extensions/codex/src/app-server/thread-supervision.ts
3252
3804
  async function materializePendingSupervisionBranch(params) {
3253
3805
  let pending = params.binding.pendingSupervisionBranch;
@@ -3271,6 +3823,32 @@ async function materializePendingSupervisionBranch(params) {
3271
3823
  });
3272
3824
  let bindingCommitted = false;
3273
3825
  let provisionalCleanupSafe = true;
3826
+ let cleanupExpected = pending;
3827
+ const trackPendingSupervisionArtifacts = async (cleanupThreadIds) => {
3828
+ const expected = pending;
3829
+ pending = withPendingSupervisionCleanup(pending, cleanupThreadIds);
3830
+ let updated;
3831
+ try {
3832
+ updated = await params.bindingStore.mutate(params.bindingIdentity, {
3833
+ kind: "patch-pending-supervision-branch",
3834
+ expected,
3835
+ pending
3836
+ });
3837
+ } catch (error) {
3838
+ try {
3839
+ const current = await params.bindingStore.read(params.bindingIdentity);
3840
+ if (matchesPendingSupervisionState(current, pending)) cleanupExpected = pending;
3841
+ else if (matchesPendingSupervisionState(current, expected)) cleanupExpected = expected;
3842
+ else throw new CodexThreadBindingConflictError(pending.sourceThreadId, "verifying supervised Codex cleanup tracking");
3843
+ } catch (verificationError) {
3844
+ provisionalCleanupSafe = false;
3845
+ throw new CodexAppServerUnsafeSubscriptionError(`Codex supervised branch cleanup tracking could not be verified: ${cleanupThreadIds.join(", ")}`, { cause: new AggregateError([error, verificationError], void 0, { cause: error }) });
3846
+ }
3847
+ throw error;
3848
+ }
3849
+ cleanupExpected = updated ? pending : void 0;
3850
+ if (!updated) throw new CodexThreadBindingConflictError(pending.sourceThreadId, "tracking supervised Codex branch cleanup");
3851
+ };
3274
3852
  try {
3275
3853
  const probeParams = buildPendingSupervisionProbeForkParams(params, pending);
3276
3854
  const rawProbeResponse = await params.lifecycleTiming.measure("supervision-model-probe-fork", async () => {
@@ -3286,10 +3864,17 @@ async function materializePendingSupervisionBranch(params) {
3286
3864
  sourceThreadId: pending.sourceThreadId,
3287
3865
  role: "model probe"
3288
3866
  });
3289
- pending = await trackPendingSupervisionArtifacts(params, pending, [probeThreadId]);
3867
+ let probeResponse;
3868
+ try {
3869
+ params.throwIfAborted();
3870
+ probeResponse = assertCodexThreadForkResponse(rawProbeResponse);
3871
+ if (params.restrictedToolSurface) await params.lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, probeThreadId, probeParams.config ?? void 0, params.signal));
3872
+ } finally {
3873
+ await unsubscribeCodexAppServerLiveThread(params.client, probeThreadId, CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS).catch((cause) => {
3874
+ throw new CodexAppServerUnsafeSubscriptionError(`Codex model probe subscription could not be released: ${probeThreadId}`, { cause });
3875
+ });
3876
+ }
3290
3877
  params.throwIfAborted();
3291
- const probeResponse = assertCodexThreadForkResponse(rawProbeResponse);
3292
- if (params.restrictedToolSurface) await params.lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, probeThreadId, probeParams.config ?? void 0, params.signal));
3293
3878
  const nativeModel = requireNonBlankSupervisionValue(probeResponse.model, "native model");
3294
3879
  const nativeModelProvider = requireNativeSupervisionModelProvider({
3295
3880
  responseModelProvider: probeResponse.modelProvider,
@@ -3335,7 +3920,7 @@ async function materializePendingSupervisionBranch(params) {
3335
3920
  otherThreadId: probeThreadId,
3336
3921
  role: "canonical branch"
3337
3922
  });
3338
- pending = await trackPendingSupervisionArtifacts(params, pending, [probeThreadId, finalThreadId]);
3923
+ await trackPendingSupervisionArtifacts([finalThreadId]);
3339
3924
  params.throwIfAborted();
3340
3925
  assertExactSupervisionModelSelection(assertCodexThreadStartResponse(rawStartResponse), {
3341
3926
  model: nativeModel,
@@ -3355,11 +3940,11 @@ async function materializePendingSupervisionBranch(params) {
3355
3940
  client: params.client,
3356
3941
  threadId: finalThreadId,
3357
3942
  ephemeral: startParams.ephemeral === true
3358
- }) || !await archiveSupervisionArtifact(params.client, probeThreadId)) {
3943
+ })) {
3359
3944
  provisionalCleanupSafe = false;
3360
3945
  throw new CodexAppServerUnsafeSubscriptionError("Codex supervised plugin app attestation cleanup failed", { cause: error });
3361
3946
  }
3362
- pending = await trackPendingSupervisionArtifacts(params, pending, []);
3947
+ await trackPendingSupervisionArtifacts([]);
3363
3948
  throw error;
3364
3949
  }
3365
3950
  if (history.responseItems.length > 0) {
@@ -3369,8 +3954,6 @@ async function materializePendingSupervisionBranch(params) {
3369
3954
  }, { signal: params.signal }));
3370
3955
  params.throwIfAborted();
3371
3956
  }
3372
- if (!await archiveSupervisionArtifact(params.client, probeThreadId)) throw new Error(`Failed to archive temporary Codex model probe: ${probeThreadId}`);
3373
- pending = await trackPendingSupervisionArtifacts(params, pending, [finalThreadId]);
3374
3957
  const historyCoveredThrough = (/* @__PURE__ */ new Date()).toISOString();
3375
3958
  const bindingModelProvider = params.normalizeBindingModelProvider(params.attempt.authProfileId, nativeModelProvider);
3376
3959
  let committed = false;
@@ -3432,31 +4015,28 @@ async function materializePendingSupervisionBranch(params) {
3432
4015
  };
3433
4016
  } catch (error) {
3434
4017
  if (bindingCommitted) throw error;
3435
- if (error instanceof CodexThreadBindingConflictAfterCleanupError) throw error;
3436
4018
  if (!provisionalCleanupSafe) {
3437
4019
  await params.abandonClient();
3438
4020
  throw error;
3439
4021
  }
3440
4022
  const cleanup = await cleanPendingSupervisionArtifacts(params.client, pending);
4023
+ const nextPending = withPendingSupervisionCleanup(pending, cleanup.remaining);
3441
4024
  let cleanupStateError;
3442
- if (cleanup.remaining.length !== (pending.cleanupThreadIds?.length ?? 0)) {
3443
- const nextPending = withPendingSupervisionCleanup(pending, cleanup.remaining);
3444
- try {
3445
- if (await params.bindingStore.mutate(params.bindingIdentity, {
3446
- kind: "patch-pending-supervision-branch",
3447
- expected: pending,
3448
- pending: nextPending
3449
- })) pending = nextPending;
3450
- } catch (stateError) {
3451
- cleanupStateError = stateError;
3452
- }
4025
+ if (cleanupExpected && !isDeepStrictEqual(cleanupExpected, nextPending)) try {
4026
+ await params.bindingStore.mutate(params.bindingIdentity, {
4027
+ kind: "patch-pending-supervision-branch",
4028
+ expected: cleanupExpected,
4029
+ pending: nextPending
4030
+ });
4031
+ } catch (stateError) {
4032
+ cleanupStateError = stateError;
3453
4033
  }
3454
4034
  const unsafeCleanup = cleanup.remaining.length > 0 || isCodexAppServerUnsafeSubscriptionError(error);
3455
4035
  if (unsafeCleanup) await params.abandonClient();
3456
4036
  if (cleanupStateError) {
3457
- const cause = new AggregateError([error, cleanupStateError]);
4037
+ const cause = new AggregateError([error, cleanupStateError], "Codex supervised branch cleanup state could not be recorded", { cause: error });
3458
4038
  if (unsafeCleanup) throw new CodexAppServerUnsafeSubscriptionError("Codex supervised branch cleanup state could not be recorded", { cause });
3459
- throw new AggregateError([error, cleanupStateError], "Codex supervised branch cleanup state could not be recorded", { cause: error });
4039
+ throw cause;
3460
4040
  }
3461
4041
  if (cleanup.remaining.length > 0) throw new CodexAppServerUnsafeSubscriptionError(`Codex supervised branch cleanup remains pending: ${cleanup.remaining.join(", ")}`, { cause: error });
3462
4042
  throw error;
@@ -3484,7 +4064,7 @@ function buildPendingSupervisionProbeForkParams(params, pending) {
3484
4064
  ...params.appServer.serviceTier !== void 0 ? { serviceTier: params.appServer.serviceTier } : {},
3485
4065
  config: runtimeConfig,
3486
4066
  developerInstructions: params.developerInstructions ?? buildDeveloperInstructions(params.attempt, { dynamicTools: params.dynamicTools }),
3487
- ephemeral: isIncognitoSessionKey(params.attempt.sessionKey),
4067
+ ephemeral: true,
3488
4068
  threadSource: "appServer",
3489
4069
  excludeTurns: true
3490
4070
  };
@@ -3526,10 +4106,8 @@ function requireDistinctSupervisionThreadId(params) {
3526
4106
  return threadId;
3527
4107
  }
3528
4108
  function readSupervisionResponseThreadId(value) {
3529
- if (!value || typeof value !== "object" || Array.isArray(value)) return;
3530
- const thread = value.thread;
3531
- if (!thread || typeof thread !== "object" || Array.isArray(thread)) return;
3532
- return thread.id;
4109
+ const thread = isRecord(value) ? value.thread : void 0;
4110
+ return isRecord(thread) ? thread.id : void 0;
3533
4111
  }
3534
4112
  async function recoverPendingSupervisionArtifacts(params, pending) {
3535
4113
  if (!pending.cleanupThreadIds?.length) return pending;
@@ -3552,20 +4130,6 @@ async function recoverPendingSupervisionArtifacts(params, pending) {
3552
4130
  })) throw new CodexThreadBindingConflictError(pending.sourceThreadId, "recovering a supervised Codex branch");
3553
4131
  return next;
3554
4132
  }
3555
- async function trackPendingSupervisionArtifacts(params, pending, cleanupThreadIds) {
3556
- const next = withPendingSupervisionCleanup(pending, cleanupThreadIds);
3557
- if (!await params.bindingStore.mutate(params.bindingIdentity, {
3558
- kind: "patch-pending-supervision-branch",
3559
- expected: pending,
3560
- pending: next
3561
- })) {
3562
- const cleanupFailed = [];
3563
- for (const threadId of cleanupThreadIds) if (!await archiveSupervisionArtifact(params.client, threadId)) cleanupFailed.push(threadId);
3564
- if (cleanupFailed.length > 0) throw new CodexAppServerUnsafeSubscriptionError(`Codex supervised branch CAS cleanup failed: ${cleanupFailed.join(", ")}`);
3565
- throw new CodexThreadBindingConflictAfterCleanupError(pending.sourceThreadId, "tracking supervised Codex branch cleanup");
3566
- }
3567
- return next;
3568
- }
3569
4133
  function withPendingSupervisionCleanup(pending, cleanupThreadIds) {
3570
4134
  return {
3571
4135
  sourceThreadId: pending.sourceThreadId,
@@ -3602,15 +4166,10 @@ async function archiveSupervisionArtifact(client, threadId) {
3602
4166
  async function startOrResumeThread(params) {
3603
4167
  const incognito = isIncognitoSessionKey(params.params.sessionKey);
3604
4168
  const clientId = resolveCodexAppServerClientInstanceId(params.client);
3605
- const bindingIdentity = sessionBindingIdentity({
3606
- sessionId: params.params.sessionId,
3607
- sessionKey: params.params.sessionKey,
3608
- agentId: params.agentId ?? params.params.agentId,
3609
- config: params.params.config
3610
- });
3611
- return await params.bindingStore.withLease(bindingIdentity, async () => {
3612
- const { contextEngineBinding, dynamicToolsContainDeferred, dynamicToolsFingerprint, environmentSelectionFingerprint, hostSystemAgentActive, legacyDynamicToolsFingerprint, legacyUserMcpServersFingerprint, lifecycleTiming, nativeSkillIsolation, nativeSkillIsolationFingerprint, networkProxyConfigFingerprint, ringZeroActive, ringZeroClientInstanceId, ringZeroConfigFingerprint, restrictedToolSurface, restrictedToolSurfaceInheritedMcpServerNames, userMcpServersConfigPatch, userMcpServersFingerprint, webSearchThreadConfigFingerprint } = await prepareCodexThreadLifecyclePreflight(params);
3613
- let binding = await lifecycleTiming.measure("read-binding", () => params.bindingStore.read(bindingIdentity));
4169
+ return await withCodexThreadLifecycleBinding(params, async (bindingIdentity, currentBinding) => {
4170
+ let binding = currentBinding;
4171
+ const preflight = await prepareCodexThreadLifecyclePreflight(params);
4172
+ const { contextEngineBinding, dynamicToolsContainDeferred, dynamicToolsFingerprint, environmentSelectionFingerprint, hostSystemAgentActive, legacyDynamicToolsFingerprint, legacyUserMcpServersFingerprint, lifecycleTiming, nativeSkillIsolation, nativeSkillIsolationFingerprint, networkProxyConfigFingerprint, ringZeroActive, ringZeroClientInstanceId, ringZeroConfigFingerprint, restrictedToolSurface, restrictedToolSurfaceInheritedMcpServerNames, userMcpServersConfigPatch, userMcpServersFingerprint, webSearchThreadConfigFingerprint } = preflight;
3614
4173
  let replacementPredecessor;
3615
4174
  const initialBoundThreadId = binding?.threadId;
3616
4175
  const initialBoundClientId = binding?.clientId;
@@ -3622,26 +4181,33 @@ async function startOrResumeThread(params) {
3622
4181
  config: params.params.config
3623
4182
  });
3624
4183
  const throwIfAborted = () => throwIfCodexThreadLifecycleAborted(params.signal);
3625
- const releaseRetainedThread = async (threadId, ownerClientId = initialBoundClientId) => {
4184
+ const releaseRetainedThread = async (threadId, ownerClientId = initialBoundClientId, assertCurrent) => {
3626
4185
  if (ownerClientId && ownerClientId !== clientId) {
3627
4186
  const previousClient = retainSharedCodexAppServerClientByInstanceId(ownerClientId);
3628
- if (!previousClient) return;
4187
+ if (!previousClient) return false;
3629
4188
  try {
3630
- await releaseCodexRetainedLiveThread({
4189
+ const assertPrevious = assertCurrent ? captureExclusiveSharedCodexAppServerClient(previousClient.client) : void 0;
4190
+ if (isCodexAppServerLiveThreadClaimed(previousClient.client, threadId)) throw new Error(`Codex thread ${threadId} is claimed by active work; stop it first.`);
4191
+ return await releaseCodexRetainedLiveThread({
3631
4192
  client: previousClient.client,
3632
4193
  lifecycleTiming,
3633
- threadId
4194
+ threadId,
4195
+ assertCurrent: assertCurrent ? () => {
4196
+ assertCurrent();
4197
+ assertPrevious?.();
4198
+ } : void 0
3634
4199
  });
3635
4200
  } finally {
3636
4201
  previousClient.release();
3637
4202
  }
3638
- return;
3639
4203
  }
3640
- await releaseCodexRetainedLiveThread({
4204
+ if (isCodexAppServerLiveThreadClaimed(params.client, threadId)) throw new Error(`Codex thread ${threadId} is claimed by active work; stop it first.`);
4205
+ return await releaseCodexRetainedLiveThread({
3641
4206
  client: params.client,
3642
4207
  abandonClient: params.abandonClient,
3643
4208
  lifecycleTiming,
3644
- threadId
4209
+ threadId,
4210
+ assertCurrent
3645
4211
  });
3646
4212
  };
3647
4213
  if (!binding && bindingIdentity.kind === "session" && bindingIdentity.sessionKey) {
@@ -3723,6 +4289,36 @@ async function startOrResumeThread(params) {
3723
4289
  })) throw new CodexThreadBindingConflictError(current.threadId, operation);
3724
4290
  binding = void 0;
3725
4291
  };
4292
+ const resolveRequestContext = () => {
4293
+ const startModelSelection = resolveCodexAppServerThreadModelSelection({
4294
+ provider: params.params.provider,
4295
+ model: params.runtimeModelId ?? params.params.modelId,
4296
+ binding,
4297
+ authProfileId: params.params.authProfileId,
4298
+ authProfileStore: params.params.authProfileStore,
4299
+ agentDir: params.params.agentDir,
4300
+ config: params.params.config
4301
+ });
4302
+ return {
4303
+ ...preflight,
4304
+ bindingIdentity,
4305
+ startModelSelection,
4306
+ startModelProvider: startModelSelection.modelProvider,
4307
+ normalizeBindingModelProvider,
4308
+ throwIfAborted
4309
+ };
4310
+ };
4311
+ const transientDelegationRestriction = params.params.delegationCapability === "report_only";
4312
+ const persistentWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed === false;
4313
+ const transientNativeToolRestriction = params.nativeCodeModeEnabled === false && !persistentWebSearchRestriction;
4314
+ const transientWebSearchRestriction = isTransientWebSearchRestriction(params);
4315
+ if (binding?.pendingResumeConfiguration) return await resumePendingCodexThread(params, {
4316
+ ...resolveRequestContext(),
4317
+ binding,
4318
+ clearCurrentBinding,
4319
+ releaseRetainedThread: (threadId, assertCurrent) => releaseRetainedThread(threadId, initialBoundClientId, assertCurrent),
4320
+ transientRestriction: transientDelegationRestriction || transientNativeToolRestriction || transientWebSearchRestriction
4321
+ });
3726
4322
  if (binding?.threadId && !restrictedToolSurface && binding.nativeToolPolicyRestricted === true) await clearCurrentBinding("rotating a host-policy-restricted thread binding");
3727
4323
  if (binding?.threadId && binding.nativeSkillIsolationFingerprint !== nativeSkillIsolationFingerprint) {
3728
4324
  embeddedAgentLog.debug("codex app-server native skill isolation changed; starting a new thread", { threadId: binding.threadId });
@@ -3756,24 +4352,30 @@ async function startOrResumeThread(params) {
3756
4352
  await clearCurrentBinding("rotating a GPT-5.6 multi-agent thread binding");
3757
4353
  binding = void 0;
3758
4354
  }
3759
- const startModelSelection = resolveCodexAppServerThreadModelSelection({
3760
- provider: params.params.provider,
3761
- model: params.runtimeModelId ?? params.params.modelId,
3762
- binding,
3763
- authProfileId: params.params.authProfileId,
3764
- authProfileStore: params.params.authProfileStore,
3765
- agentDir: params.params.agentDir,
3766
- config: params.params.config
3767
- });
3768
- const startModelProvider = startModelSelection.modelProvider;
3769
- const transientDelegationRestriction = params.params.delegationCapability === "report_only";
4355
+ const requestContext = resolveRequestContext();
3770
4356
  let preserveExistingBinding = transientDelegationRestriction || !ringZeroActive && params.nativeProviderWebSearchSupport === "unknown" && !binding?.threadId;
3771
4357
  let rotatedContextEngineBinding = false;
3772
4358
  let prebuiltPluginThreadConfig;
4359
+ const buildLoadedPluginThreadConfig = async (current) => {
4360
+ if (!params.pluginThreadConfig?.requiresCurrentPolicyCheck && !shouldRecheckRecoverablePluginBinding({
4361
+ binding: current,
4362
+ pluginThreadConfig: params.pluginThreadConfig
4363
+ })) return;
4364
+ try {
4365
+ prebuiltPluginThreadConfig = await lifecycleTiming.measure("plugin-config-recovery", () => params.pluginThreadConfig?.build({ threadId: current.threadId }));
4366
+ } catch (error) {
4367
+ throwIfAborted();
4368
+ if (params.pluginThreadConfig?.requiresCurrentPolicyCheck) throw error;
4369
+ embeddedAgentLog.warn("codex app-server plugin app config recovery check failed", {
4370
+ error,
4371
+ threadId: current.threadId
4372
+ });
4373
+ return;
4374
+ }
4375
+ throwIfAborted();
4376
+ return prebuiltPluginThreadConfig;
4377
+ };
3773
4378
  const webSearchBindingChanged = binding?.threadId && binding.webSearchThreadConfigFingerprint !== webSearchThreadConfigFingerprint;
3774
- const persistentWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed === false;
3775
- const transientNativeToolRestriction = params.nativeCodeModeEnabled === false && !persistentWebSearchRestriction;
3776
- const transientWebSearchRestriction = isTransientWebSearchRestriction(params);
3777
4379
  const explicitTransientWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed !== false && transientWebSearchRestriction;
3778
4380
  const unknownProviderWebSearchSupport = params.nativeProviderWebSearchSupport === "unknown";
3779
4381
  if (binding?.threadId && (params.configuredMcpOwnershipVersion === 1 && (binding.configuredMcpOwnershipVersion !== 1 || binding.dynamicToolsFingerprint === void 0 || binding.mcpServersFingerprint !== void 0 || binding.userMcpServersFingerprint !== void 0) || params.configuredMcpOwnershipVersion !== 1 && binding.configuredMcpOwnershipVersion === 1) && binding?.threadId) {
@@ -3851,28 +4453,13 @@ async function startOrResumeThread(params) {
3851
4453
  binding = void 0;
3852
4454
  }
3853
4455
  if (binding?.threadId) {
3854
- let pluginBindingStale = isCodexPluginThreadBindingStale({
4456
+ if (isCodexPluginThreadBindingStale({
3855
4457
  codexPluginsEnabled: params.pluginThreadConfig?.enabled ?? false,
3856
4458
  bindingFingerprint: binding.pluginAppsFingerprint,
3857
4459
  bindingInputFingerprint: binding.pluginAppsInputFingerprint,
3858
4460
  currentInputFingerprint: params.pluginThreadConfig?.inputFingerprint,
3859
4461
  hasBindingPolicyContext: Boolean(binding.pluginAppPolicyContext)
3860
- });
3861
- if (!pluginBindingStale && (params.pluginThreadConfig?.requiresCurrentPolicyCheck || shouldRecheckRecoverablePluginBinding({
3862
- binding,
3863
- pluginThreadConfig: params.pluginThreadConfig
3864
- }))) try {
3865
- const bindingThreadId = binding.threadId;
3866
- prebuiltPluginThreadConfig = await lifecycleTiming.measure("plugin-config-recovery", () => params.pluginThreadConfig?.build({ threadId: bindingThreadId }));
3867
- pluginBindingStale = prebuiltPluginThreadConfig?.fingerprint !== binding.pluginAppsFingerprint;
3868
- } catch (error) {
3869
- if (params.pluginThreadConfig?.requiresCurrentPolicyCheck) throw error;
3870
- embeddedAgentLog.warn("codex app-server plugin app config recovery check failed", {
3871
- error,
3872
- threadId: binding.threadId
3873
- });
3874
- }
3875
- if (pluginBindingStale) {
4462
+ })) {
3876
4463
  embeddedAgentLog.debug("codex app-server plugin app config changed; starting a new thread", { threadId: binding.threadId });
3877
4464
  await clearCurrentBinding("rotating a stale thread binding");
3878
4465
  binding = void 0;
@@ -3885,120 +4472,70 @@ async function startOrResumeThread(params) {
3885
4472
  binding = void 0;
3886
4473
  }
3887
4474
  }
3888
- if (binding?.threadId) if (binding.dynamicToolsFingerprint && !areDynamicToolFingerprintsCompatible(binding.dynamicToolsFingerprint, dynamicToolsFingerprint, legacyDynamicToolsFingerprint)) {
3889
- assertCodexBindingMayBeReplaced(binding, "changing the dynamic tool catalog");
3890
- preserveExistingBinding = shouldStartTransientNoToolThread({
3891
- previous: binding.dynamicToolsFingerprint,
3892
- nextHasDynamicTools: params.dynamicTools.length > 0
3893
- });
3894
- if (preserveExistingBinding) embeddedAgentLog.debug("codex app-server dynamic tools unavailable for turn; starting transient thread", { threadId: binding.threadId });
3895
- else {
3896
- embeddedAgentLog.debug("codex app-server dynamic tool catalog changed; starting a new thread", { threadId: binding.threadId });
3897
- await clearCurrentBinding("rotating a stale thread binding");
3898
- }
3899
- } else if (incognito) {
3900
- if (binding.clientId && binding.clientId === clientId) {
3901
- params.buildFinalConfigPatch?.({
3902
- action: "resume",
3903
- binding
4475
+ if (binding?.threadId) {
4476
+ if (binding.dynamicToolsFingerprint && !areDynamicToolFingerprintsCompatible(binding.dynamicToolsFingerprint, dynamicToolsFingerprint, legacyDynamicToolsFingerprint)) {
4477
+ assertCodexBindingMayBeReplaced(binding, "changing the dynamic tool catalog");
4478
+ preserveExistingBinding = shouldStartTransientNoToolThread({
4479
+ previous: binding.dynamicToolsFingerprint,
4480
+ nextHasDynamicTools: params.dynamicTools.length > 0
3904
4481
  });
3905
- throwIfAborted();
3906
- lifecycleTiming.mark("thread-ready");
3907
- lifecycleTiming.logSummary({
3908
- runId: params.params.runId,
3909
- sessionId: params.params.sessionId,
3910
- sessionKey: params.params.sessionKey,
3911
- threadId: binding.threadId,
3912
- action: "resumed"
4482
+ if (preserveExistingBinding) embeddedAgentLog.debug("codex app-server dynamic tools unavailable for turn; starting transient thread", { threadId: binding.threadId });
4483
+ else {
4484
+ embeddedAgentLog.debug("codex app-server dynamic tool catalog changed; starting a new thread", { threadId: binding.threadId });
4485
+ await clearCurrentBinding("rotating a stale thread binding");
4486
+ }
4487
+ } else if (incognito) {
4488
+ if (binding.clientId && binding.clientId === clientId && ((await buildLoadedPluginThreadConfig(binding))?.fingerprint ?? binding.pluginAppsFingerprint) === binding.pluginAppsFingerprint) {
4489
+ params.buildFinalConfigPatch?.({
4490
+ action: "resume",
4491
+ binding
4492
+ });
4493
+ throwIfAborted();
4494
+ lifecycleTiming.mark("thread-ready");
4495
+ lifecycleTiming.logSummary({
4496
+ runId: params.params.runId,
4497
+ sessionId: params.params.sessionId,
4498
+ sessionKey: params.params.sessionKey,
4499
+ threadId: binding.threadId,
4500
+ action: "resumed"
4501
+ });
4502
+ return {
4503
+ ...binding,
4504
+ lifecycle: { action: "resumed" }
4505
+ };
4506
+ }
4507
+ await clearCurrentBinding("rotating an unavailable ephemeral thread binding");
4508
+ binding = void 0;
4509
+ } else {
4510
+ const warmReuse = await tryReuseCodexLiveThread({
4511
+ ...requestContext,
4512
+ params,
4513
+ binding,
4514
+ clientId,
4515
+ buildLoadedPluginThreadConfig
3913
4516
  });
3914
- return {
3915
- ...binding,
3916
- lifecycle: { action: "resumed" }
3917
- };
4517
+ if (warmReuse.kind === "ready") return warmReuse.binding;
4518
+ if (warmReuse.kind === "rotate") {
4519
+ throwIfAborted();
4520
+ await clearCurrentBinding("rotating a stale plugin app binding");
4521
+ } else {
4522
+ if (binding.preserveNativeModel === true) await assertAdoptedCodexThreadResumeAllowed(params, binding.threadId, requestContext);
4523
+ await releaseRetainedThread(binding.threadId);
4524
+ const resumed = await resumeExistingCodexThread(params, {
4525
+ ...requestContext,
4526
+ binding,
4527
+ clearCurrentBinding,
4528
+ prebuiltFinalConfigPatch: warmReuse.prebuiltFinalConfigPatch,
4529
+ prebuiltPluginThreadConfig,
4530
+ buildLoadedPluginThreadConfig
4531
+ });
4532
+ if (resumed) return resumed;
4533
+ }
3918
4534
  }
3919
- await clearCurrentBinding("rotating an unavailable ephemeral thread binding");
3920
- binding = void 0;
3921
- } else {
3922
- const warmReuse = await tryReuseCodexLiveThread({
3923
- params,
3924
- binding,
3925
- bindingIdentity,
3926
- clientId,
3927
- dynamicToolsFingerprint,
3928
- environmentSelectionFingerprint,
3929
- hostSystemAgentActive,
3930
- lifecycleTiming,
3931
- nativeSkillIsolation,
3932
- releaseConsumedThread: (threadId, cause) => releaseCodexConsumedLiveThread({
3933
- client: params.client,
3934
- abandonClient: params.abandonClient,
3935
- lifecycleTiming,
3936
- threadId,
3937
- cause
3938
- }),
3939
- ringZeroActive,
3940
- restrictedToolSurfaceInheritedMcpServerNames,
3941
- startModelProvider,
3942
- startModelSelection,
3943
- throwIfAborted,
3944
- userMcpServersConfigPatch
3945
- });
3946
- if (warmReuse.binding) return warmReuse.binding;
3947
- await releaseRetainedThread(binding.threadId);
3948
- const resumed = await resumeExistingCodexThread(params, {
3949
- binding,
3950
- bindingIdentity,
3951
- startModelSelection,
3952
- startModelProvider,
3953
- userMcpServersConfigPatch,
3954
- dynamicToolsFingerprint,
3955
- dynamicToolsContainDeferred,
3956
- webSearchThreadConfigFingerprint,
3957
- nativeSkillIsolationFingerprint,
3958
- userMcpServersFingerprint,
3959
- ringZeroConfigFingerprint,
3960
- ringZeroClientInstanceId,
3961
- networkProxyConfigFingerprint,
3962
- contextEngineBinding,
3963
- environmentSelectionFingerprint,
3964
- hostSystemAgentActive,
3965
- ringZeroActive,
3966
- restrictedToolSurface,
3967
- restrictedToolSurfaceInheritedMcpServerNames,
3968
- nativeSkillIsolation,
3969
- lifecycleTiming,
3970
- normalizeBindingModelProvider,
3971
- throwIfAborted,
3972
- clearCurrentBinding,
3973
- prebuiltFinalConfigPatch: warmReuse.prebuiltFinalConfigPatch,
3974
- prebuiltPluginThreadConfig
3975
- });
3976
- if (resumed) return resumed;
3977
4535
  }
3978
4536
  if (initialBoundThreadId && !preserveExistingBinding && !replacementPredecessor) await releaseRetainedThread(initialBoundThreadId);
3979
4537
  const started = await startFreshCodexThread(params, {
3980
- bindingIdentity,
3981
- startModelSelection,
3982
- startModelProvider,
3983
- userMcpServersConfigPatch,
3984
- dynamicToolsFingerprint,
3985
- dynamicToolsContainDeferred,
3986
- webSearchThreadConfigFingerprint,
3987
- nativeSkillIsolationFingerprint,
3988
- userMcpServersFingerprint,
3989
- ringZeroConfigFingerprint,
3990
- ringZeroClientInstanceId,
3991
- networkProxyConfigFingerprint,
3992
- contextEngineBinding,
3993
- environmentSelectionFingerprint,
3994
- hostSystemAgentActive,
3995
- ringZeroActive,
3996
- restrictedToolSurface,
3997
- restrictedToolSurfaceInheritedMcpServerNames,
3998
- nativeSkillIsolation,
3999
- lifecycleTiming,
4000
- normalizeBindingModelProvider,
4001
- throwIfAborted,
4538
+ ...requestContext,
4002
4539
  prebuiltPluginThreadConfig,
4003
4540
  preserveExistingBinding,
4004
4541
  rotatedContextEngineBinding,
@@ -4063,10 +4600,17 @@ function buildTurnStartParams(params, options) {
4063
4600
  });
4064
4601
  const useThreadPermissionProfile = options.appServer.networkProxy && !options.sandboxPolicy;
4065
4602
  const currentSenderContext = params.trigger === "user" ? buildCodexCurrentSenderContextValue(params) : void 0;
4066
- const additionalContext = currentSenderContext ? { openclaw_current_sender: {
4603
+ let additionalContext = currentSenderContext ? { openclaw_current_sender: {
4067
4604
  kind: "untrusted",
4068
4605
  value: currentSenderContext
4069
4606
  } } : void 0;
4607
+ if (params.permissionChange?.notice) additionalContext = {
4608
+ ...additionalContext,
4609
+ openclaw_permission_change: {
4610
+ kind: "application",
4611
+ value: params.permissionChange.notice
4612
+ }
4613
+ };
4070
4614
  return {
4071
4615
  threadId: options.threadId,
4072
4616
  input: [...buildCodexUserInput(options.promptText ?? params.prompt, params.images), ...options.explicitSkillInputs ?? []],
@@ -4105,7 +4649,6 @@ function buildTurnCollaborationMode(params, options = {}) {
4105
4649
  function buildTurnScopedCollaborationInstructions(params, options = {}) {
4106
4650
  const contextInstructions = joinPresentSections(options.turnScopedDeveloperInstructions, options.memoryCollaborationInstructions, options.skillsCollaborationInstructions);
4107
4651
  if (params.trigger === "cron") return joinPresentSections(buildCronCollaborationInstructions(), contextInstructions);
4108
- if (params.trigger === "heartbeat") return joinPresentSections(buildHeartbeatCollaborationInstructions(), contextInstructions);
4109
4652
  if (contextInstructions?.trim()) return joinPresentSections(buildDefaultCollaborationInstructions(), contextInstructions);
4110
4653
  return null;
4111
4654
  }
@@ -4132,15 +4675,8 @@ function buildCronCollaborationInstructions() {
4132
4675
  "Keep output concise and automation-oriented. Prefer the final command result or a short failure summary over status narration."
4133
4676
  ].join("\n\n");
4134
4677
  }
4135
- function buildHeartbeatCollaborationInstructions() {
4136
- return [
4137
- "This is an OpenClaw heartbeat turn. Apply these instructions only to this heartbeat wake; ordinary chat turns should stay in Codex Default mode.",
4138
- "When you are ready to end the heartbeat, prefer the structured `heartbeat_respond` tool so OpenClaw can record the wake outcome and notification decision. If `heartbeat_respond` is not already available and `tool_search` is available, search for `heartbeat_respond`, load it, then call it. Use `notify=false` when nothing should visibly interrupt the user.",
4139
- GPT5_HEARTBEAT_PROMPT_OVERLAY
4140
- ].join("\n\n");
4141
- }
4142
4678
  function joinPresentSections(...sections) {
4143
4679
  return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
4144
4680
  }
4145
4681
  //#endregion
4146
- export { ensureCodexPluginActivation as $, isForcedPrivateQaCodexRuntime as A, neutralizeCodexExplicitMentionSigils as B, CODEX_MANAGED_THREAD_NAMESPACE as C, codexLegacyDynamicToolsFingerprint as D, codexDynamicToolsFingerprint as E, resolveCodexDynamicToolsLoadingForRuntime as F, buildCodexPluginAppsConfigPatchFromPolicyContext as G, resolveCodexContextEngineProjectionMaxChars as H, buildContextEngineBinding as I, buildCodexPluginThreadConfigTimeoutFallback as J, buildCodexPluginThreadConfig as K, isContextEngineBindingCompatible as L, isSystemAgentOnlyCodexDynamicToolAllowlist as M, normalizeCodexDynamicToolName as N, filterCodexDynamicTools as O, resolveCodexDynamicToolsLoading as P, shouldBuildCodexPluginThreadConfig as Q, buildCodexContinuityCalibration as R, CODEX_MANAGED_THREAD_MAX_ENTRIES as S, areCodexDynamicToolFingerprintsCompatible as T, resolveCodexContextEngineProjectionReserveTokens as U, projectContextEngineAssemblyForCodex as V, resolveCodexContinuityProjectionMaxChars as W, buildPluginAppPolicyContext as X, buildDisabledAppsConfigPatch as Y, mergeCodexThreadConfigs as Z, resolveCodexAppServerRequestModelSelection as _, isCodexAppServerProfilerEnabled as a, resolveReasoningEffort as b, buildCodexRingZeroThreadConfigPatch as c, buildCodexNativeWebSearchThreadConfig as d, refreshCodexPluginRuntimeState as et, resolveCodexWebSearchPlan as f, resolveCodexAppServerModelProvider as g, CODEX_NATIVE_PERSONALITY_NONE as h, startOrResumeThread as i, resolveRecoverableCodexPluginConfigKeys as it, isMessageOnlyCodexSourceReply as j, filterCodexDynamicToolsForDisabledNativeSurface as k, buildCodexRuntimeThreadConfig as l, buildCodexProjectDocThreadConfig as m, buildTurnStartParams as n, isOpenAiCuratedMarketplaceName as nt, assertCodexManagedRequirementsDoNotOverrideToolPolicy as o, buildDeveloperInstructions as p, buildCodexPluginThreadConfigInputFingerprint as q, buildCodexUserInput as r, pluginReadParams as rt, attestCodexRestrictedToolSurfaceMcpServersDisabled as s, buildTurnCollaborationMode as t, isOpenAiCuratedMarketplace as tt, readCodexInheritedMcpServerNames as u, resolveCodexAppServerThreadModelSelection as v, createCodexManagedThreadStore as w, readCodexSupportedReasoningEfforts as x, resolveCodexBindingModelProviderFallback as y, fitCodexProjectedContextForTurnStart as z };
4682
+ export { resolveCodexContinuityProjectionMaxChars as $, readCodexSupportedReasoningEfforts as A, isMessageOnlyCodexSourceReply as B, buildCodexProjectDocThreadConfig as C, resolveCodexAppServerThreadModelSelection as D, resolveCodexAppServerRequestModelSelection as E, codexDynamicToolsFingerprint as F, buildContextEngineBinding as G, normalizeCodexDynamicToolName as H, codexLegacyDynamicToolsFingerprint as I, fitCodexProjectedContextForTurnStart as J, isContextEngineBindingCompatible as K, filterCodexDynamicTools as L, CODEX_MANAGED_THREAD_NAMESPACE as M, createCodexManagedThreadStore as N, resolveCodexBindingModelProviderFallback as O, areCodexDynamicToolFingerprintsCompatible as P, resolveCodexContextEngineProjectionReserveTokens as Q, filterCodexDynamicToolsForDisabledNativeSurface as R, buildDeveloperInstructions as S, resolveCodexAppServerModelProvider as T, resolveCodexDynamicToolsLoading as U, isSystemAgentOnlyCodexDynamicToolAllowlist as V, resolveCodexDynamicToolsLoadingForRuntime as W, projectContextEngineAssemblyForCodex as X, neutralizeCodexExplicitMentionSigils as Y, resolveCodexContextEngineProjectionMaxChars as Z, buildCodexRingZeroThreadConfigPatch as _, isCodexAppServerProfilerEnabled as a, buildPluginAppPolicyContext as at, buildCodexNativeWebSearchThreadConfig as b, buildCodexNativeHookRelayConfig as c, ensureCodexPluginActivation as ct, emitCodexNativePreToolUseFailureDiagnostic as d, isOpenAiCuratedMarketplaceName as dt, buildCodexPluginAppsConfigPatchFromPolicyContext as et, resolveCodexNativeHookRelayEvents as f, pluginReadParams as ft, attestCodexRestrictedToolSurfaceMcpServersDisabled as g, assertCodexManagedRequirementsDoNotOverrideToolPolicy as h, startOrResumeThread as i, buildDisabledAppsConfigPatch as it, CODEX_MANAGED_THREAD_MAX_ENTRIES as j, resolveReasoningEffort as k, buildCodexNativeHookRelayDisabledConfig as l, refreshCodexPluginRuntimeState as lt, scheduleCodexNativeHookRelayUnregister as m, buildTurnStartParams as n, buildCodexPluginThreadConfigInputFingerprint as nt, CODEX_NATIVE_HOOK_RELAY_EVENTS as o, mergeCodexThreadConfigs as ot, resolveCodexNativeHookRelayTtlMs as p, resolveRecoverableCodexPluginConfigKeys as pt, buildCodexContinuityCalibration as q, buildCodexUserInput as r, buildCodexPluginThreadConfigTimeoutFallback as rt, CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS as s, shouldBuildCodexPluginThreadConfig as st, buildTurnCollaborationMode as t, buildCodexPluginThreadConfig as tt, createCodexNativeHookRelay as u, isOpenAiCuratedMarketplace as ut, buildCodexRuntimeThreadConfig as v, CODEX_NATIVE_PERSONALITY_NONE as w, resolveCodexWebSearchPlan as x, readCodexInheritedMcpServerNames as y, isForcedPrivateQaCodexRuntime as z };