@openclaw/codex 2026.8.1-beta.2 → 2026.8.1

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 (64) hide show
  1. package/dist/api.js +146 -0
  2. package/dist/{attempt-client-cleanup-DMLXUfa8.js → attempt-client-cleanup-CVPvp9dL.js} +47 -5
  3. package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
  4. package/dist/{binding-connection-I5Bo-AsX.js → binding-connection-CCQFs8lf.js} +15 -3
  5. package/dist/{bounded-turn-qgZmzy_4.js → bounded-turn-rq5tiSXk.js} +27 -26
  6. package/dist/{capabilities-CybGI1oz.js → capabilities-CGm_94eL.js} +1 -1
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-B1AEz8ZA.js → command-formatters-C54Yg3eA.js} +2 -2
  9. package/dist/{command-handlers-Db8pu_ds.js → command-handlers-BvOAHTrA.js} +111 -67
  10. package/dist/{compact-LspQaGfl.js → compact-Hd7SAc0N.js} +35 -30
  11. package/dist/{session-binding-DPxIvMgE.js → config-Cxw3Cl3k.js} +361 -1161
  12. package/dist/context-compaction-activity-BOeJf7yo.js +58 -0
  13. package/dist/doctor-contract-api.js +128 -26
  14. package/dist/{dynamic-tools-BVdyWkUD.js → dynamic-tools-BFoUcRdx.js} +1341 -1464
  15. package/dist/{effective-mcp-catalog-DIIhAtOZ.js → effective-mcp-catalog-BTJHnDSx.js} +2 -2
  16. package/dist/elicitation-input-DMkeki8d.js +71 -0
  17. package/dist/{event-projector-assistant-message-BwxV4d6N.js → event-projector-assistant-message-C5-Ds8mA.js} +18 -1
  18. package/dist/{event-projector-usage-e5huIQWu.js → event-projector-usage-D3PHFFXh.js} +18 -18
  19. package/dist/harness-DVfUQBlQ.js +248 -0
  20. package/dist/harness.js +2 -226
  21. package/dist/index.js +405 -299
  22. package/dist/{isolated-completion-C58NbL40.js → isolated-completion-Cbbx_5Ed.js} +4 -4
  23. package/dist/launch-args-sXOAg0SX.js +84 -0
  24. package/dist/managed-binary-ZkGodW19.js +248 -0
  25. package/dist/media-understanding-provider.js +2 -2
  26. package/dist/model-catalog-o6Cgjoss.js +101 -0
  27. package/dist/{models-BFlpn1Ex.js → models-DgFWSuxA.js} +25 -28
  28. package/dist/{native-subagent-monitor-DyTGmTUx.js → native-subagent-monitor-CwFyoylz.js} +385 -94
  29. package/dist/node-exec-server.runtime-DXrqpw1c.js +195 -0
  30. package/dist/plugin-approval-roundtrip-D80UZiJ_.js +95 -0
  31. package/dist/{plugin-marketplace-discovery-CzrGJdCR.js → plugin-marketplace-discovery-mdmScYOj.js} +60 -55
  32. package/dist/{plugin-metadata-cache-CNJPj4_b.js → plugin-metadata-cache-CulOABwu.js} +164 -134
  33. package/dist/{protocol-validators-B4F7L8Rf.js → protocol-validators-Bg5rpNxk.js} +203 -7
  34. package/dist/{rate-limits-DZXmOL8-.js → rate-limits-hFtzt2Up.js} +1 -1
  35. package/dist/{sandbox-guard-Cmwc9eIk.js → request-CCm4LaED.js} +207 -20
  36. package/dist/{run-attempt-P8BdxC1I.js → run-attempt-BwNQDH2u.js} +1506 -836
  37. package/dist/{runtime-artifact-BVSPdd6g.js → runtime-artifact-DeBvleJw.js} +4 -7
  38. package/dist/session-binding-q2WSTVmC.js +947 -0
  39. package/dist/{session-catalog-xwNi_SDM.js → session-catalog-C2Tae4rI.js} +1698 -1346
  40. package/dist/session-catalog-home-id-DAKUrm3C.js +112 -0
  41. package/dist/{session-cli-7hgXZDV7.js → session-cli-BuDjm5Ou.js} +20 -7
  42. package/dist/session-permission-policy-gLm40Vtq.js +160 -0
  43. package/dist/session-retirement-CdkI0bvb.js +90 -0
  44. package/dist/{session-upstream-marker-CrIFX06J.js → session-upstream-marker-Bcy33kam.js} +2 -3
  45. package/dist/{settled-turn-finalizer-BRWBO4MO.js → settled-turn-finalizer-B8JJcxCi.js} +27 -22
  46. package/dist/{shared-client-kIBbGjk4.js → shared-client-kNkljY-R.js} +2023 -607
  47. package/dist/{side-question-9HAL26l9.js → side-question-GwD73tX6.js} +109 -57
  48. package/dist/{thread-lifecycle-Bu1BTDXr.js → thread-lifecycle-Ss-uwnC4.js} +1208 -497
  49. package/dist/{thread-ownership-6kOQHlyC.js → thread-ownership-CeYBqg_4.js} +18 -8
  50. package/dist/{plugin-app-cache-key-C3H2CVp6.js → thread-resume-DF54hagX.js} +64 -6
  51. package/dist/{transcript-mirror-B9gGgnJS.js → transcript-mirror-CkEQIna-.js} +158 -41
  52. package/dist/transport-stdio-C7-s9TLG.js +708 -0
  53. package/dist/{upstream-prompt-provenance-ClRBije0.js → upstream-prompt-provenance-CFmsKwr9.js} +12 -14
  54. package/dist/{upstream-session-fork-CE-_LdnP.js → upstream-session-fork-BACGlOFa.js} +86 -88
  55. package/dist/{usage-CHlV0e-o.js → usage-C2l8Mils.js} +3 -3
  56. package/dist/{web-search-provider.runtime-DRw2Vl42.js → web-search-provider.runtime-B2XgRdFv.js} +3 -3
  57. package/openclaw.plugin.json +50 -2
  58. package/package.json +9 -8
  59. package/dist/app-server-policy-zcBDc1JU.js +0 -49
  60. package/dist/incognito-session-ET_ufDH-.js +0 -2
  61. package/dist/request-BRl5_UfX.js +0 -157
  62. package/dist/session-retirement-DoMMJFKm.js +0 -36
  63. package/dist/thread-resume-CPrJNQVM.js +0 -30
  64. package/dist/transport-stdio-DIImwDVS.js +0 -99
@@ -1,27 +1,38 @@
1
- import { D as CODEX_PLUGINS_MARKETPLACE_NAME, E as resolveCodexPluginsPolicy, O as CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME, a as hashCodexAppServerBindingFingerprint, c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, i as createCodexSessionGenerationSupersededError, m as codexSandboxPolicyForTurn, n as assertCodexBindingMayBeReplaced, o as isCodexAppServerNativeAuthProfile, s as normalizeCodexAppServerBindingModelProvider } from "./session-binding-DPxIvMgE.js";
2
- import { M as isCodexAppServerConnectionClosedError, R as resolveCodexAppServerClientInstanceId, V as consumeCodexAppServerLiveThread, ct as isJsonObject, d as retainSharedCodexAppServerClientByInstanceId, k as getCodexAppServerClientInstanceId, s as isCodexAppServerStartSelectionChangedError, st as flattenCodexDynamicToolFunctions, z as CodexAppServerRpcError } from "./shared-client-kIBbGjk4.js";
3
- 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-DMLXUfa8.js";
4
- import { a as assertCodexThreadStartResponse, r as assertCodexThreadForkResponse } from "./protocol-validators-B4F7L8Rf.js";
5
- import { a as serializeCodexAppInventoryError, i as defaultCodexAppInventoryCache, t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-C3H2CVp6.js";
6
- import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
7
- import { t as resumeCodexAppServerThread } from "./thread-resume-CPrJNQVM.js";
8
- import { u as projectBoundedCodexThreadHistory } from "./transcript-mirror-B9gGgnJS.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-Cxw3Cl3k.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 } from "./shared-client-kNkljY-R.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-q2WSTVmC.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-CVPvp9dL.js";
6
+ import { c as serializeCodexAppInventoryError, n as buildCodexAppServerConnectionFingerprint, s as defaultCodexAppInventoryCache, t as resumeCodexAppServerThread } from "./thread-resume-DF54hagX.js";
7
+ import { c as withExclusiveCodexAppServerThread, l as isIncognitoSessionKey } from "./thread-ownership-CeYBqg_4.js";
8
+ import { i as readCodexSessionMeta, n as codexCatalogHomeId } from "./session-catalog-home-id-DAKUrm3C.js";
9
+ import { d as projectBoundedCodexThreadHistory } from "./transcript-mirror-CkEQIna-.js";
9
10
  import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-CnfoUPCE.js";
10
11
  import { asOptionalRecord, isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
11
12
  import fs from "node:fs/promises";
12
13
  import path from "node:path";
13
- import { SKILL_WORKSHOP_TOOL_NAME, TRANSCRIPT_CREDENTIAL_SAFETY_PROMPT, buildSkillWorkshopPromptSection, embeddedAgentLog, formatErrorMessage, isActiveHarnessContextEngine, isHostScopedAgentToolActive } 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";
14
17
  import * as crypto$1 from "node:crypto";
15
- import crypto from "node:crypto";
18
+ import crypto, { createHash } from "node:crypto";
16
19
  import os from "node:os";
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";
23
+ import { z } from "zod";
17
24
  import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
18
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";
19
28
  import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
29
+ import { isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
20
30
  import { resolveRequiredHomeDir, resolveStateDir } from "openclaw/plugin-sdk/state-paths";
21
31
  import { listRegisteredPluginAgentPromptGuidance } from "openclaw/plugin-sdk/plugin-runtime";
22
- import { buildCodexUserMcpServersThreadConfigPatchForRuntime } from "openclaw/plugin-sdk/codex-mcp-projection";
23
- import { isDiagnosticFlagEnabled } from "openclaw/plugin-sdk/diagnostic-runtime";
24
- import { GPT5_HEARTBEAT_PROMPT_OVERLAY } from "openclaw/plugin-sdk/provider-model-shared";
32
+ import { buildHostnameAllowlistPolicyFromSuffixAllowlist } from "openclaw/plugin-sdk/ssrf-policy";
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";
25
36
  //#region extensions/codex/src/app-server/plugin-inventory.ts
26
37
  /**
27
38
  * Reads Codex plugin marketplace state and app inventory to decide which
@@ -264,15 +275,39 @@ function resolveOwnedApps(params) {
264
275
  enabled: false,
265
276
  needsAuth: true
266
277
  };
267
- return {
278
+ return Object.assign({
268
279
  id: app.id,
269
280
  name: app.name,
270
281
  accessible: info.isAccessible,
271
282
  enabled: info.isEnabled,
272
283
  needsAuth: !info.isAccessible
273
- };
284
+ }, resolveOwnedAppReadOnlyToolConfigKeys(info));
274
285
  }).toSorted((left, right) => left.id.localeCompare(right.id));
275
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
+ }
276
311
  function findPluginSummary(marketplace, pluginName) {
277
312
  const exact = marketplace.plugins.find((plugin) => plugin.id === pluginName || plugin.id === `${pluginName}@${marketplace.name}`);
278
313
  if (exact) return exact;
@@ -451,12 +486,21 @@ function activationFailure(identity, reason, diagnostic, extraDiagnostics = [])
451
486
  }
452
487
  //#endregion
453
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
+ }
454
498
  async function refreshCodexPluginAppInventory(params, appCache, options = {}) {
455
499
  if (!params.appCacheKey) return;
456
- const request = async (method, requestParams) => await params.request(method, requestParams);
500
+ const request = createCodexPluginThreadAppInventoryRequest(params);
457
501
  try {
458
502
  return await appCache.refreshNow({
459
- key: params.appCacheKey,
503
+ key: resolveCodexPluginThreadAppCacheKey(params),
460
504
  request,
461
505
  nowMs: params.nowMs,
462
506
  forceRefetch: options.forceRefetch,
@@ -493,9 +537,9 @@ function normalizeCodexPluginOwnerName(name) {
493
537
  return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
494
538
  }
495
539
  async function readCodexThreadAdmissibleAccountApps(params, appCache) {
496
- const request = async (method, requestParams) => await params.request(method, requestParams);
540
+ const request = createCodexPluginThreadAppInventoryRequest(params);
497
541
  const cachedInventory = appCache.read({
498
- key: params.appCacheKey,
542
+ key: resolveCodexPluginThreadAppCacheKey(params),
499
543
  request,
500
544
  nowMs: params.nowMs,
501
545
  suppressRefresh: true
@@ -521,7 +565,8 @@ function toCodexPluginOwnedAccountApp(app) {
521
565
  name: app.name,
522
566
  accessible: app.isAccessible,
523
567
  enabled: app.isEnabled,
524
- needsAuth: !app.isAccessible
568
+ needsAuth: !app.isAccessible,
569
+ ...resolveOwnedAppReadOnlyToolConfigKeys(app)
525
570
  };
526
571
  }
527
572
  function resolveCodexThreadConfigAppsForRecord(params) {
@@ -610,6 +655,11 @@ function buildCodexPluginThreadConfigTimeoutFallback(params) {
610
655
  /** Builds the Codex apps config patch and policy context for a native thread. */
611
656
  async function buildCodexPluginThreadConfig(params) {
612
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);
613
663
  let inputFingerprint = buildCodexPluginThreadConfigInputFingerprint({
614
664
  pluginConfig: params.pluginConfig,
615
665
  appCacheKey: params.appCacheKey
@@ -623,9 +673,9 @@ async function buildCodexPluginThreadConfig(params) {
623
673
  let inventory = policy.pluginPolicies.length > 0 ? await readCodexPluginInventory({
624
674
  pluginConfig: params.pluginConfig,
625
675
  policy,
626
- request: params.request,
676
+ request: threadRequest,
627
677
  appCache,
628
- appCacheKey: params.appCacheKey,
678
+ appCacheKey: threadAppCacheKey,
629
679
  configCwd: params.configCwd,
630
680
  metadataCache: params.metadataCache,
631
681
  nowMs: params.nowMs,
@@ -641,9 +691,9 @@ async function buildCodexPluginThreadConfig(params) {
641
691
  inventory = await readCodexPluginInventory({
642
692
  pluginConfig: params.pluginConfig,
643
693
  policy,
644
- request: params.request,
694
+ request: threadRequest,
645
695
  appCache,
646
- appCacheKey: params.appCacheKey,
696
+ appCacheKey: threadAppCacheKey,
647
697
  configCwd: params.configCwd,
648
698
  metadataCache: params.metadataCache,
649
699
  nowMs: params.nowMs
@@ -659,9 +709,9 @@ async function buildCodexPluginThreadConfig(params) {
659
709
  if (!record.activationRequired) continue;
660
710
  const activation = await ensureCodexPluginActivation({
661
711
  identity: record.policy,
662
- request: params.request,
712
+ request: threadRequest,
663
713
  appCache,
664
- appCacheKey: params.appCacheKey,
714
+ appCacheKey: threadAppCacheKey,
665
715
  configCwd: params.configCwd,
666
716
  metadataCache: params.metadataCache,
667
717
  deferAppInventoryRefresh: true,
@@ -685,9 +735,9 @@ async function buildCodexPluginThreadConfig(params) {
685
735
  inventory = await readCodexPluginInventory({
686
736
  pluginConfig: params.pluginConfig,
687
737
  policy,
688
- request: params.request,
738
+ request: threadRequest,
689
739
  appCache,
690
- appCacheKey: params.appCacheKey,
740
+ appCacheKey: threadAppCacheKey,
691
741
  configCwd: params.configCwd,
692
742
  metadataCache: params.metadataCache,
693
743
  nowMs: params.nowMs
@@ -706,9 +756,9 @@ async function buildCodexPluginThreadConfig(params) {
706
756
  inventory = await readCodexPluginInventory({
707
757
  pluginConfig: params.pluginConfig,
708
758
  policy,
709
- request: params.request,
759
+ request: threadRequest,
710
760
  appCache,
711
- appCacheKey: params.appCacheKey,
761
+ appCacheKey: threadAppCacheKey,
712
762
  configCwd: params.configCwd,
713
763
  metadataCache: params.metadataCache,
714
764
  nowMs: params.nowMs
@@ -946,7 +996,7 @@ function readPersistedAppToolApprovalOverrideNames(config, app) {
946
996
  const appConfig = isJsonObject(appsRoot) ? appsRoot[app.id] : void 0;
947
997
  const tools = isJsonObject(appConfig) ? appConfig.tools : void 0;
948
998
  if (!isJsonObject(tools)) return [];
949
- 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();
950
1000
  }
951
1001
  function hasPersistedToolApprovalOverride(value) {
952
1002
  return isJsonObject(value) && value.approval_mode !== void 0;
@@ -1063,6 +1113,32 @@ function resolveCodexContextEngineProjectionMaxChars(params) {
1063
1113
  function resolveCodexContextEngineProjectionReserveTokens() {
1064
1114
  return DEFAULT_CODEX_PROJECTION_RESERVE_TOKENS;
1065
1115
  }
1116
+ const CONTINUITY_PROJECTION_RESERVE_RATIO = .5;
1117
+ const CONTINUITY_EMPIRICAL_CHARS_PER_TOKEN = 3;
1118
+ const CONTINUITY_MIN_CHARS_PER_TOKEN = .5;
1119
+ const CONTINUITY_MAX_CHARS_PER_TOKEN = CONTINUITY_EMPIRICAL_CHARS_PER_TOKEN;
1120
+ const CONTINUITY_CALIBRATION_MIN_PROMPT_CHARS = 5e4;
1121
+ /** Builds a calibration sample from a completed turn, or undefined if unusable. */
1122
+ function buildCodexContinuityCalibration(params) {
1123
+ if (!Number.isFinite(params.promptChars) || !Number.isFinite(params.inputTokens) || params.promptChars < CONTINUITY_CALIBRATION_MIN_PROMPT_CHARS || params.inputTokens <= 0) return;
1124
+ return {
1125
+ promptChars: Math.floor(params.promptChars),
1126
+ inputTokens: Math.floor(params.inputTokens)
1127
+ };
1128
+ }
1129
+ function resolveContinuityCharsPerToken(calibration) {
1130
+ if (!calibration || !Number.isFinite(calibration.promptChars) || !Number.isFinite(calibration.inputTokens) || calibration.promptChars < CONTINUITY_CALIBRATION_MIN_PROMPT_CHARS || calibration.inputTokens <= 0) return CONTINUITY_EMPIRICAL_CHARS_PER_TOKEN;
1131
+ return Math.min(CONTINUITY_MAX_CHARS_PER_TOKEN, Math.max(CONTINUITY_MIN_CHARS_PER_TOKEN, calibration.promptChars / calibration.inputTokens));
1132
+ }
1133
+ /** Resolves rendered context size for no-engine continuity projections. */
1134
+ function resolveCodexContinuityProjectionMaxChars(params) {
1135
+ const contextTokenBudget = typeof params.contextTokenBudget === "number" && Number.isFinite(params.contextTokenBudget) ? Math.floor(params.contextTokenBudget) : void 0;
1136
+ if (!contextTokenBudget || contextTokenBudget <= 0) return DEFAULT_RENDERED_CONTEXT_CHARS;
1137
+ return normalizeRenderedContextMaxChars(resolveProjectionPromptBudgetTokens({
1138
+ contextTokenBudget,
1139
+ reserveTokens: Math.max(DEFAULT_CODEX_PROJECTION_RESERVE_TOKENS, Math.floor(contextTokenBudget * CONTINUITY_PROJECTION_RESERVE_RATIO))
1140
+ }) * resolveContinuityCharsPerToken(params.calibration));
1141
+ }
1066
1142
  /** Fits projected context prompts under Codex app-server turn/start text limits. */
1067
1143
  function fitCodexProjectedContextForTurnStart(params) {
1068
1144
  const maxChars = typeof params.maxChars === "number" && Number.isFinite(params.maxChars) ? Math.max(0, Math.floor(params.maxChars)) : CODEX_TURN_START_TEXT_INPUT_MAX_CHARS;
@@ -1328,7 +1404,6 @@ const CODEX_APP_SERVER_OWNED_REPLACEABLE_TOOL_EXCLUDES = /* @__PURE__ */ new Set
1328
1404
  "write",
1329
1405
  "edit",
1330
1406
  "apply_patch",
1331
- "update_plan",
1332
1407
  ...CODEX_NATIVE_GOAL_TOOL_EXCLUDES
1333
1408
  ]);
1334
1409
  const CODEX_APP_SERVER_OWNED_SHELL_TOOL_EXCLUDES = /* @__PURE__ */ new Set(["exec", "process"]);
@@ -1561,10 +1636,6 @@ const nativeSkillIsolationByClient = /* @__PURE__ */ new WeakMap();
1561
1636
  function isMissingPathError(error) {
1562
1637
  return error.code === "ENOENT";
1563
1638
  }
1564
- function isPathWithin(root, candidate) {
1565
- const relative = path.relative(path.resolve(root), path.resolve(candidate));
1566
- return relative === "" || !path.isAbsolute(relative) && !relative.startsWith(`..${path.sep}`) && relative !== "..";
1567
- }
1568
1639
  async function canonicalizeExistingPath(candidate) {
1569
1640
  try {
1570
1641
  return await fs.realpath(candidate);
@@ -1590,12 +1661,12 @@ async function collectPersonalSkillRealPaths(homes, codexHome) {
1590
1661
  const realDefaultCodexHome = await canonicalizeExistingPath(defaultCodexHome);
1591
1662
  roots.push({
1592
1663
  dir: path.join(defaultCodexHome, "skills"),
1593
- onlyEscapedStateTargets: isPathWithin(realStateDir, realDefaultCodexHome)
1664
+ onlyEscapedStateTargets: isPathInside(realStateDir, realDefaultCodexHome)
1594
1665
  });
1595
1666
  }
1596
1667
  const configuredCodexHome = codexHome?.trim() || process.env.CODEX_HOME?.trim();
1597
1668
  if (configuredCodexHome) {
1598
- const stateOwned = isPathWithin(realStateDir, await canonicalizeExistingPath(configuredCodexHome));
1669
+ const stateOwned = isPathInside(realStateDir, await canonicalizeExistingPath(configuredCodexHome));
1599
1670
  roots.push({
1600
1671
  dir: path.join(configuredCodexHome, "skills"),
1601
1672
  onlyEscapedStateTargets: stateOwned
@@ -1613,7 +1684,7 @@ async function collectPersonalSkillRealPaths(homes, codexHome) {
1613
1684
  const recordSkillFile = async (filePath, onlyEscapedStateTargets) => {
1614
1685
  try {
1615
1686
  const skillRealPath = await fs.realpath(filePath);
1616
- if (!onlyEscapedStateTargets || !isPathWithin(realStateDir, skillRealPath)) skillPaths.add(skillRealPath);
1687
+ if (!onlyEscapedStateTargets || !isPathInside(realStateDir, skillRealPath)) skillPaths.add(skillRealPath);
1617
1688
  } catch (error) {
1618
1689
  if (!isMissingPathError(error)) complete = false;
1619
1690
  }
@@ -1753,32 +1824,63 @@ function applyCodexNativeSkillIsolation(config, isolation) {
1753
1824
  };
1754
1825
  }
1755
1826
  //#endregion
1756
- //#region extensions/codex/src/app-server/thread-lifecycle-errors.ts
1757
- var CodexThreadStartRequestError = class extends Error {
1758
- constructor(cause) {
1759
- super(formatErrorMessage(cause), { cause });
1760
- this.name = "CodexThreadStartRequestError";
1761
- }
1762
- };
1763
- var CodexThreadBindingConflictError = class extends Error {
1764
- constructor(threadId, operation) {
1765
- super(`Codex thread binding changed while ${operation}: ${threadId}`);
1766
- this.name = "CodexThreadBindingConflictError";
1767
- }
1768
- };
1769
- var CodexRestrictedToolSurfaceAttestationError = class extends Error {
1770
- constructor(cause) {
1771
- super("Codex restricted-tool-surface MCP attestation failed", { cause });
1772
- this.name = "CodexRestrictedToolSurfaceAttestationError";
1773
- }
1774
- };
1775
- var CodexThreadBindingConflictAfterCleanupError = class extends CodexThreadBindingConflictError {};
1776
- var CodexAdoptedThreadActiveError = class extends Error {
1777
- constructor() {
1778
- super("Codex session became active in another runner; wait for it to finish before continuing");
1779
- this.name = "CodexAdoptedThreadActiveError";
1827
+ //#region extensions/codex/src/app-server/managed-thread-store.ts
1828
+ const CODEX_MANAGED_THREAD_NAMESPACE = "app-server-managed-threads";
1829
+ const CODEX_MANAGED_THREAD_MAX_ENTRIES = 2e4;
1830
+ const managedThreadSchema = z.object({
1831
+ version: z.literal(1),
1832
+ kind: z.literal("managed-thread"),
1833
+ sourceHomeId: z.string().min(1),
1834
+ threadId: z.string().min(1),
1835
+ rolloutPath: z.string().min(1).optional()
1836
+ });
1837
+ async function markStartedCodexManagedThread(store, params) {
1838
+ if (!store) return;
1839
+ try {
1840
+ await store.mark({
1841
+ sourceHomeId: params.sourceHomeId,
1842
+ threadId: params.threadId,
1843
+ ...params.rolloutPath ? { rolloutPath: params.rolloutPath } : {}
1844
+ });
1845
+ } catch (error) {
1846
+ embeddedAgentLog.warn("failed to record Codex managed thread ownership", { error });
1780
1847
  }
1781
- };
1848
+ }
1849
+ function managedThreadStoreKey(sourceHomeId, threadId) {
1850
+ return `sha256:${createHash("sha256").update("openclaw:codex-managed-thread:v1\0").update(sourceHomeId).update("\0").update(threadId).digest("hex")}`;
1851
+ }
1852
+ /** Durable ownership index for Codex threads created by OpenClaw. */
1853
+ function createCodexManagedThreadStore(state) {
1854
+ return {
1855
+ async mark(params) {
1856
+ try {
1857
+ const value = managedThreadSchema.parse({
1858
+ version: 1,
1859
+ kind: "managed-thread",
1860
+ sourceHomeId: params.sourceHomeId.trim(),
1861
+ threadId: params.threadId.trim(),
1862
+ ...params.rolloutPath?.trim() ? { rolloutPath: params.rolloutPath.trim() } : {}
1863
+ });
1864
+ state.registerIfAbsent(managedThreadStoreKey(value.sourceHomeId, value.threadId), value);
1865
+ return true;
1866
+ } catch (error) {
1867
+ embeddedAgentLog.warn("failed to record Codex managed thread ownership", { error });
1868
+ return false;
1869
+ }
1870
+ },
1871
+ async snapshot() {
1872
+ const byHome = /* @__PURE__ */ new Map();
1873
+ for (const entry of state.entries()) {
1874
+ const parsed = managedThreadSchema.safeParse(entry.value);
1875
+ if (!parsed.success) continue;
1876
+ const ids = byHome.get(parsed.data.sourceHomeId) ?? /* @__PURE__ */ new Set();
1877
+ ids.add(parsed.data.threadId);
1878
+ byHome.set(parsed.data.sourceHomeId, ids);
1879
+ }
1880
+ return byHome;
1881
+ }
1882
+ };
1883
+ }
1782
1884
  //#endregion
1783
1885
  //#region extensions/codex/src/app-server/plugin-thread-attestation.ts
1784
1886
  /**
@@ -1835,6 +1937,50 @@ async function discardUnattestedCodexPluginThread(params) {
1835
1937
  }
1836
1938
  }
1837
1939
  //#endregion
1940
+ //#region extensions/codex/src/app-server/thread-lifecycle-result.ts
1941
+ /** Materializes the public lifecycle result after a fresh thread is durably committed. */
1942
+ function buildStartedCodexThreadBinding(input) {
1943
+ const { context, params, response, startParams } = input;
1944
+ return {
1945
+ threadId: response.thread.id,
1946
+ ...input.clientId ? { clientId: input.clientId } : {},
1947
+ cwd: params.cwd,
1948
+ ...input.rolloutPath ? { rolloutPath: input.rolloutPath } : {},
1949
+ authProfileId: params.params.authProfileId,
1950
+ agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
1951
+ model: response.model ?? startParams.model ?? params.params.modelId,
1952
+ modelProvider: response.modelProvider ?? input.startModelProvider ?? input.modelProvider,
1953
+ dynamicToolsFingerprint: context.dynamicToolsFingerprint,
1954
+ dynamicToolsContainDeferred: context.dynamicToolsContainDeferred,
1955
+ nativeSkillIsolationFingerprint: context.nativeSkillIsolationFingerprint,
1956
+ userMcpServersFingerprint: context.userMcpServersFingerprint,
1957
+ mcpServersFingerprint: input.nextMcpServersFingerprint,
1958
+ configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
1959
+ ringZeroConfigFingerprint: context.ringZeroConfigFingerprint,
1960
+ ringZeroClientInstanceId: context.ringZeroClientInstanceId,
1961
+ networkProxyProfileName: params.appServer.networkProxy?.profileName,
1962
+ networkProxyConfigFingerprint: context.networkProxyConfigFingerprint,
1963
+ nativeHookRelayGeneration: input.finalConfigPatch.nativeHookRelayGeneration,
1964
+ appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
1965
+ pluginAppsFingerprint: input.pluginThreadConfig?.fingerprint,
1966
+ pluginAppsInputFingerprint: input.pluginThreadConfig?.inputFingerprint,
1967
+ pluginAppPolicyContext: input.pluginThreadConfig?.policyContext,
1968
+ contextEngine: context.contextEngineBinding,
1969
+ environmentSelectionFingerprint: context.environmentSelectionFingerprint,
1970
+ ...!context.preserveExistingBinding ? { liveThreadConfigFingerprint: fingerprintCodexThreadConfig({
1971
+ ...startParams,
1972
+ model: response.model ?? startParams.model ?? null,
1973
+ requestedModel: startParams.model ?? null,
1974
+ modelProvider: input.bindingModelProvider ?? null,
1975
+ requestedModelProvider: startParams.modelProvider ?? input.bindingModelProvider ?? null
1976
+ }, params.params.authProfileId, context.dynamicToolsFingerprint) } : {},
1977
+ lifecycle: {
1978
+ action: "started",
1979
+ ...context.rotatedContextEngineBinding ? { rotatedContextEngineBinding: true } : {}
1980
+ }
1981
+ };
1982
+ }
1983
+ //#endregion
1838
1984
  //#region extensions/codex/src/app-server/reasoning-effort.ts
1839
1985
  const CODEX_REASONING_EFFORTS = [
1840
1986
  "minimal",
@@ -1852,11 +1998,6 @@ const LEGACY_PRO_REASONING_EFFORTS = [
1852
1998
  ];
1853
1999
  const LEGACY_PRO_MODEL_ID_RE = /^gpt-5\.[45]-pro$/u;
1854
2000
  const MODERN_GPT_5_MODEL_ID_RE = /^gpt-5\.(?:[3-9]|[1-9]\d)(?:$|-)/u;
1855
- function normalizeCodexReasoningEfforts(efforts) {
1856
- if (!efforts) return [];
1857
- const supported = new Set(efforts.map((effort) => effort.trim().toLowerCase()));
1858
- return CODEX_REASONING_EFFORTS.filter((effort) => supported.has(effort));
1859
- }
1860
2001
  /** Read reasoning metadata after the Codex app-server route has been selected. */
1861
2002
  function readCodexSupportedReasoningEfforts(compat) {
1862
2003
  if (!compat || typeof compat !== "object" || Array.isArray(compat)) return;
@@ -1865,7 +2006,8 @@ function readCodexSupportedReasoningEfforts(compat) {
1865
2006
  return efforts.filter((effort) => typeof effort === "string");
1866
2007
  }
1867
2008
  function resolveSupportedReasoningEffort(params) {
1868
- const supported = normalizeCodexReasoningEfforts(params.supportedReasoningEfforts);
2009
+ const declared = new Set(params.supportedReasoningEfforts.map((effort) => effort.trim().toLowerCase()));
2010
+ const supported = CODEX_REASONING_EFFORTS.filter((effort) => declared.has(effort));
1869
2011
  if (supported.includes(params.requested)) return params.requested;
1870
2012
  const fallbackEfforts = params.requested === "ultra" ? supported : supported.filter((effort) => effort !== "ultra");
1871
2013
  const requestedRank = CODEX_REASONING_EFFORTS.indexOf(params.requested);
@@ -1873,7 +2015,8 @@ function resolveSupportedReasoningEffort(params) {
1873
2015
  }
1874
2016
  /** Resolve a turn effort from the selected model's provider-owned metadata. */
1875
2017
  function resolveCodexAppServerReasoningEffort(params) {
1876
- if (params.thinkLevel === "off" || params.thinkLevel === "adaptive") return null;
2018
+ if (params.thinkLevel === "off") return params.supportedReasoningEfforts?.includes("none") ? "none" : null;
2019
+ if (params.thinkLevel === "adaptive") return null;
1877
2020
  if (params.supportedReasoningEfforts) return resolveSupportedReasoningEffort({
1878
2021
  requested: params.thinkLevel,
1879
2022
  supportedReasoningEfforts: params.supportedReasoningEfforts
@@ -1963,12 +2106,22 @@ function resolveReasoningEffort(thinkLevel, modelId, supportedReasoningEfforts)
1963
2106
  });
1964
2107
  }
1965
2108
  //#endregion
2109
+ //#region extensions/codex/src/app-server/project-doc-thread-config.ts
2110
+ const CODEX_NATIVE_PROJECT_DOC_MAX_BYTES = 128 * 1024;
2111
+ function buildCodexProjectDocThreadConfig(config) {
2112
+ const defaults = { project_doc_max_bytes: CODEX_NATIVE_PROJECT_DOC_MAX_BYTES };
2113
+ return mergeCodexThreadConfigs(defaults, config) ?? defaults;
2114
+ }
2115
+ //#endregion
1966
2116
  //#region extensions/codex/src/app-server/thread-prompt.ts
1967
2117
  function buildDeveloperInstructions(params, options = {}) {
1968
2118
  const deferredToolNames = /* @__PURE__ */ new Set();
2119
+ let secretsToolName;
1969
2120
  let hasSkillWorkshop = false;
1970
2121
  let hasSessionsSpawn = false;
1971
2122
  let hasSessionsYield = false;
2123
+ let hasSubagentsList = false;
2124
+ let hasSessionsSend = false;
1972
2125
  let hasSeenDirectNamespace = false;
1973
2126
  let messageToolAvailable = options.dynamicTools ? false : params.disableMessageTool !== true;
1974
2127
  for (const spec of options.dynamicTools ?? []) {
@@ -1977,9 +2130,12 @@ function buildDeveloperInstructions(params, options = {}) {
1977
2130
  for (const tool of spec.type === "namespace" ? spec.tools : [spec]) {
1978
2131
  const name = tool.name.trim();
1979
2132
  if (tool.deferLoading === true && name) deferredToolNames.add(name);
2133
+ if (name === "secrets" && params.disableTools !== true) secretsToolName ??= spec.type === "namespace" ? `${spec.name}.${name}` : name;
1980
2134
  hasSkillWorkshop ||= name === SKILL_WORKSHOP_TOOL_NAME;
1981
2135
  hasSessionsSpawn ||= name === "sessions_spawn";
1982
2136
  hasSessionsYield ||= isDirectNamespace && name === "sessions_yield";
2137
+ hasSubagentsList ||= name === "subagents";
2138
+ hasSessionsSend ||= name === "sessions_send";
1983
2139
  messageToolAvailable ||= name === "message";
1984
2140
  }
1985
2141
  }
@@ -1987,25 +2143,71 @@ function buildDeveloperInstructions(params, options = {}) {
1987
2143
  surface: "codex_app_server",
1988
2144
  includeLegacyGlobalGuidance: false
1989
2145
  }).join("\n");
1990
- const nativeDelegationAvailable = params.disableTools !== true && params.delegationCapability !== "report_only" && !isMessageOnlyCodexSourceReply(params) && !isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow) && !shouldDisableCodexToolSearchForModel(params.modelId);
2146
+ const delegationGuidanceAvailable = params.disableTools !== true && params.delegationCapability !== "report_only" && !isMessageOnlyCodexSourceReply(params);
2147
+ const nativeDelegationAvailable = delegationGuidanceAvailable && !isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow) && !shouldDisableCodexToolSearchForModel(params.modelId);
1991
2148
  const deferredToolDiscoveryGuidance = deferredToolNames.size > 0 || nativeDelegationAvailable ? "Deferred tools may be absent from the direct tool list. Use `tool_search` when directly callable. On code-mode-only models, use `exec` instead: filter `ALL_TOOLS` by name and description, then call the matching entry through `tools`." : void 0;
1992
2149
  return [
1993
2150
  "You are a personal agent running inside OpenClaw. OpenClaw has dynamic tools for OpenClaw-owned messaging, cron, sessions, media, gateway, and nodes.",
1994
2151
  deferredToolNames.size > 0 ? `Deferred searchable OpenClaw dynamic tools available: ${[...deferredToolNames].toSorted((left, right) => left.localeCompare(right)).join(", ")}.` : void 0,
1995
2152
  deferredToolDiscoveryGuidance,
1996
2153
  hasSkillWorkshop ? buildSkillWorkshopPromptSection().join("\n") : void 0,
1997
- nativeDelegationAvailable ? `Use Codex native \`spawn_agent\` for Codex subagents. \`spawn_agent\` and the other native collaboration tools may be deferred.${hasSessionsSpawn ? " Use OpenClaw `sessions_spawn` only for OpenClaw or ACP delegation, never as a substitute for `spawn_agent`." : ""}` : void 0,
2154
+ nativeDelegationAvailable ? `Use Codex native \`spawn_agent\` for Codex subagents. \`spawn_agent\` and the other native collaboration tools may be deferred.${hasSessionsSpawn ? " Use OpenClaw `sessions_spawn` only for OpenClaw or ACP delegation, never as a substitute for `spawn_agent` on internal legwork." : ""}` : void 0,
1998
2155
  hasSessionsYield && nativeDelegationAvailable ? "When a native child's result belongs in a later turn, end the current turn with `openclaw_direct.sessions_yield`; the completion arrives as the next model-visible input. Use native `wait_agent` only for an intentional same-turn wait when the immediate next step is blocked on the child. Never loop-poll for native child completion." : void 0,
1999
- buildVisibleReplyInstruction(params, messageToolAvailable),
2000
- TRANSCRIPT_CREDENTIAL_SAFETY_PROMPT,
2156
+ delegationGuidanceAvailable ? buildDelegationGuidanceSection({
2157
+ mode: resolveMainSessionDelegationMode({
2158
+ config: params.config,
2159
+ agentId: params.agentId,
2160
+ sessionKey: params.sessionKey
2161
+ }),
2162
+ isMinimal: params.promptMode === "minimal" || params.promptMode === "none",
2163
+ hiddenDelegationTool: nativeDelegationAvailable ? "native `spawn_agent`" : hasSessionsSpawn ? "`sessions_spawn`" : "",
2164
+ hasVisibleSessionSpawn: hasSessionsSpawn,
2165
+ hasSessionsYield,
2166
+ hasSubagentsList,
2167
+ hasSessionsSend
2168
+ }).join("\n") : void 0,
2169
+ buildHarnessVisibleReplyGuidance({
2170
+ sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
2171
+ messageToolAvailable
2172
+ }),
2173
+ buildCredentialSafetyPrompt(secretsToolName),
2001
2174
  nativeCommandGuidance,
2002
2175
  params.extraSystemPrompt
2003
2176
  ].filter((section) => typeof section === "string" && section.trim()).join("\n\n");
2004
2177
  }
2005
- function buildVisibleReplyInstruction(params, messageToolAvailable) {
2006
- if (params.sourceReplyDeliveryMode === "message_tool_only" && messageToolAvailable) return "Visible source replies are not automatically delivered for this run. Use `message(action=send)` for user-visible source-channel output. For progress, set `final=false`. Set `final=true`, or omit it, for the completed reply to the current source conversation; OpenClaw stops after confirming delivery. Do not repeat visible message content in your final answer.";
2007
- if (messageToolAvailable) return "For the current source conversation, reply normally in your final assistant message; OpenClaw will deliver it through the active source conversation. Use `message` for supported non-text actions in the current conversation, such as reacting to its current message. Reserve other `message` actions for explicit out-of-band sends or media/file delivery. Reactions are not delivered automatically.";
2008
- return "For the current source conversation, reply normally in your final assistant message; OpenClaw will deliver it through the active source conversation.";
2178
+ //#endregion
2179
+ //#region extensions/codex/src/app-server/thread-shell-environment.ts
2180
+ /** Applies host-selected values and any required login-shell restriction last. */
2181
+ function applyCodexManagedShellEnvironment(config, environment, disableLoginShell = false) {
2182
+ if (!environment || Object.keys(environment).length === 0) return disableLoginShell ? {
2183
+ ...config,
2184
+ allow_login_shell: false
2185
+ } : config;
2186
+ const current = isJsonObject(config.shell_environment_policy) ? config.shell_environment_policy : {};
2187
+ const currentSet = isJsonObject(current.set) ? current.set : {};
2188
+ const names = Object.keys(environment).toSorted();
2189
+ const includeOnly = Array.isArray(current.include_only) ? current.include_only.filter((entry) => typeof entry === "string") : [];
2190
+ const filters = isJsonObject(current.filters) ? current.filters : void 0;
2191
+ const hasIncludeFilter = filters && Object.values(filters).includes("include");
2192
+ const managedConfig = {
2193
+ ...config,
2194
+ shell_environment_policy: {
2195
+ ...current,
2196
+ experimental_use_profile: false,
2197
+ set: {
2198
+ ...currentSet,
2199
+ ...environment
2200
+ },
2201
+ ...filters ? hasIncludeFilter ? { filters: {
2202
+ ...filters,
2203
+ ...Object.fromEntries(names.map((name) => [name, "include"]))
2204
+ } } : {} : includeOnly.length > 0 ? { include_only: [.../* @__PURE__ */ new Set([...includeOnly, ...names])] } : {}
2205
+ }
2206
+ };
2207
+ return disableLoginShell ? {
2208
+ ...managedConfig,
2209
+ allow_login_shell: false
2210
+ } : managedConfig;
2009
2211
  }
2010
2212
  //#endregion
2011
2213
  //#region extensions/codex/src/app-server/web-search.ts
@@ -2068,20 +2270,23 @@ function resolveCodexWebSearchPlan(params) {
2068
2270
  return {
2069
2271
  kind: "native-hosted",
2070
2272
  suppressManagedWebSearch: true,
2071
- threadConfig: buildCodexNativeWebSearchThreadConfig(params.config)
2273
+ threadConfig: buildCodexNativeWebSearchThreadConfig(params.config),
2274
+ webFetchHostnameAllowlist: buildHostnameAllowlistPolicyFromSuffixAllowlist(nativeConfig?.allowedDomains)?.hostnameAllowlist
2072
2275
  };
2073
2276
  }
2074
2277
  const CODEX_CODE_MODE_THREAD_CONFIG = {
2075
2278
  "features.code_mode": true,
2076
2279
  "features.code_mode_only": false,
2077
- "features.apply_patch_streaming_events": true
2280
+ "features.apply_patch_streaming_events": true,
2281
+ suppress_unstable_features_warning: true
2078
2282
  };
2079
2283
  const CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG = { "features.goals": false };
2284
+ const CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG = { "tools.update_plan.enabled": false };
2080
2285
  const CODEX_CODE_MODE_DISABLED_THREAD_CONFIG = {
2081
2286
  "features.code_mode": false,
2082
2287
  "features.code_mode_only": false
2083
2288
  };
2084
- const CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG = { project_doc_max_bytes: 0 };
2289
+ const CODEX_NO_PROJECT_DOCS_CONFIG = { project_doc_max_bytes: 0 };
2085
2290
  const CODEX_TOOL_SEARCH_UNSUPPORTED_THREAD_CONFIG = { "features.multi_agent": false };
2086
2291
  const CODEX_DELEGATION_DISABLED_THREAD_CONFIG = {
2087
2292
  "agents.enabled": false,
@@ -2127,7 +2332,6 @@ const CODEX_RING_ZERO_THREAD_CONFIG = {
2127
2332
  "skills.bundled.enabled": false,
2128
2333
  "skills.include_instructions": false,
2129
2334
  "tools.experimental_request_user_input.enabled": false,
2130
- "tools.update_plan.enabled": false,
2131
2335
  hooks: {
2132
2336
  PreToolUse: [],
2133
2337
  PermissionRequest: [],
@@ -2140,7 +2344,6 @@ const CODEX_RING_ZERO_THREAD_CONFIG = {
2140
2344
  SubagentStop: [],
2141
2345
  Stop: []
2142
2346
  },
2143
- project_doc_max_bytes: 0,
2144
2347
  notify: [],
2145
2348
  web_search: "disabled"
2146
2349
  };
@@ -2153,6 +2356,7 @@ const CODEX_RING_ZERO_RESTRICTED_FEATURE_ALIASES = /* @__PURE__ */ new Map([
2153
2356
  ["codex_hooks", "hooks"]
2154
2357
  ]);
2155
2358
  const CODEX_RING_ZERO_OVERRIDABLE_LAYER_TYPES = /* @__PURE__ */ new Set([
2359
+ "packagedDefaults",
2156
2360
  "mdm",
2157
2361
  "system",
2158
2362
  "enterpriseManaged",
@@ -2181,6 +2385,7 @@ function buildThreadStartParams(params, options) {
2181
2385
  model: modelSelection.model,
2182
2386
  ...modelSelection.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
2183
2387
  cwd: options.cwd,
2388
+ ...options.appServer.sessionRoot ? { runtimeWorkspaceRoots: [options.appServer.sessionRoot] } : {},
2184
2389
  approvalPolicy: options.appServer.approvalPolicy,
2185
2390
  approvalsReviewer: resolveCodexThreadApprovalsReviewer(options.appServer, options.config),
2186
2391
  ...codexThreadSandboxOrPermissions(options.appServer),
@@ -2196,7 +2401,9 @@ function buildThreadStartParams(params, options) {
2196
2401
  webSearchAllowed: options.webSearchAllowed,
2197
2402
  appServer: options.appServer,
2198
2403
  hostSystemAgentActive: options.hostSystemAgentActive,
2199
- restrictedToolSurfaceInheritedMcpServerNames: options.restrictedToolSurfaceInheritedMcpServerNames
2404
+ restrictedToolSurfaceInheritedMcpServerNames: options.restrictedToolSurfaceInheritedMcpServerNames,
2405
+ shellEnvironment: options.shellEnvironment,
2406
+ disableLoginShell: options.disableLoginShell
2200
2407
  }),
2201
2408
  ...resolveCodexThreadEnvironmentSelection(options),
2202
2409
  developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools }),
@@ -2222,6 +2429,8 @@ function buildThreadResumeParams(params, options) {
2222
2429
  });
2223
2430
  return {
2224
2431
  threadId: options.threadId,
2432
+ ...options.cwd ? { cwd: options.cwd } : {},
2433
+ ...options.appServer.sessionRoot ? { runtimeWorkspaceRoots: [options.appServer.sessionRoot] } : {},
2225
2434
  excludeTurns: true,
2226
2435
  initialTurnsPage: {
2227
2436
  limit: 1,
@@ -2245,38 +2454,38 @@ function buildThreadResumeParams(params, options) {
2245
2454
  webSearchAllowed: options.webSearchAllowed,
2246
2455
  appServer: options.appServer,
2247
2456
  hostSystemAgentActive: options.hostSystemAgentActive,
2248
- restrictedToolSurfaceInheritedMcpServerNames: options.restrictedToolSurfaceInheritedMcpServerNames
2457
+ restrictedToolSurfaceInheritedMcpServerNames: options.restrictedToolSurfaceInheritedMcpServerNames,
2458
+ shellEnvironment: options.shellEnvironment,
2459
+ disableLoginShell: options.disableLoginShell
2249
2460
  }),
2250
2461
  developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools })
2251
2462
  };
2252
2463
  }
2253
2464
  function buildCodexRuntimeThreadConfig(config, options = {}) {
2465
+ const configured = buildCodexProjectDocThreadConfig(config);
2254
2466
  const codeModeConfig = {
2255
2467
  ...CODEX_CODE_MODE_THREAD_CONFIG,
2256
2468
  "features.code_mode_only": options.nativeCodeModeOnlyEnabled === true
2257
2469
  };
2258
2470
  if (options.nativeCodeModeEnabled === false) {
2259
- const disabledConfig = mergeCodexThreadConfigs(config, CODEX_CODE_MODE_DISABLED_THREAD_CONFIG, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG) ?? { ...CODEX_CODE_MODE_DISABLED_THREAD_CONFIG };
2471
+ 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");
2260
2472
  delete disabledConfig["features.apply_patch_streaming_events"];
2261
2473
  return disabledConfig;
2262
2474
  }
2263
- if (options.nativeCodeModeOnlyEnabled === true) return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, config, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, { "features.code_mode_only": true }) ?? {
2264
- ...codeModeConfig,
2265
- ...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG,
2266
- "features.code_mode_only": true
2267
- }, options.directOnlyToolNamespaces);
2268
- return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, config, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG) ?? {
2269
- ...codeModeConfig,
2270
- ...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG
2271
- }, options.directOnlyToolNamespaces);
2475
+ 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);
2476
+ 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);
2272
2477
  }
2273
2478
  function ensureDirectOnlyToolNamespaces(config, requiredNamespaces) {
2274
2479
  if (!requiredNamespaces?.length) return config;
2275
- const configured = config["code_mode.direct_only_tool_namespaces"];
2276
- const namespaces = Array.isArray(configured) ? configured.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
2480
+ const feature = expectDefined(config["features.code_mode"], "Codex code mode config");
2481
+ const configured = isJsonObject(feature) ? feature : { enabled: feature };
2482
+ const namespaces = Array.isArray(configured.direct_only_tool_namespaces) ? configured.direct_only_tool_namespaces.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
2277
2483
  return {
2278
2484
  ...config,
2279
- "code_mode.direct_only_tool_namespaces": [.../* @__PURE__ */ new Set([...namespaces, ...requiredNamespaces])]
2485
+ "features.code_mode": {
2486
+ ...configured,
2487
+ direct_only_tool_namespaces: [.../* @__PURE__ */ new Set([...namespaces, ...requiredNamespaces])]
2488
+ }
2280
2489
  };
2281
2490
  }
2282
2491
  function resolveDirectOnlyToolNamespaces(dynamicTools) {
@@ -2286,13 +2495,10 @@ function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
2286
2495
  const ringZeroActive = (options.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow);
2287
2496
  const messageOnlySourceReply = isMessageOnlyCodexSourceReply(params);
2288
2497
  const restrictedToolSurface = ringZeroActive || messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted === true;
2498
+ const restrictedTurnDisablesProjectDocs = ringZeroActive || messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted && params.disableTools;
2289
2499
  const configMcpServers = config?.mcp_servers;
2290
2500
  if (restrictedToolSurface && configMcpServers !== void 0 && !isJsonObject(configMcpServers)) throw new Error("Codex restricted tool surface received invalid thread mcp_servers config");
2291
2501
  const restrictedToolSurfaceMcpServerNames = [...options.restrictedToolSurfaceInheritedMcpServerNames ?? [], ...isJsonObject(configMcpServers) ? Object.keys(configMcpServers) : []];
2292
- const restrictedRunConfig = restrictedToolSurface && isJsonObject(configMcpServers) ? {
2293
- ...config,
2294
- mcp_servers: {}
2295
- } : config;
2296
2502
  const webSearchConfig = resolveCodexWebSearchPlan({
2297
2503
  config: params.config,
2298
2504
  disableTools: params.disableTools,
@@ -2300,19 +2506,20 @@ function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
2300
2506
  nativeProviderWebSearchSupport: options.nativeProviderWebSearchSupport,
2301
2507
  webSearchAllowed: options.webSearchAllowed
2302
2508
  }).threadConfig;
2303
- const baseConfig = buildCodexRuntimeThreadConfig(mergeCodexThreadConfigs(restrictedRunConfig, webSearchConfig), options);
2304
- const runtimeConfig = mergeCodexThreadConfigs(baseConfig, options.appServer?.networkProxy?.configPatch, 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 ? buildCodexRestrictedToolThreadConfigPatch(restrictedToolSurfaceMcpServerNames) : buildCodexRingZeroThreadConfigPatch(params, options.hostSystemAgentActive, restrictedToolSurfaceMcpServerNames)) ?? baseConfig;
2305
- if (params.bootstrapContextMode !== "lightweight") return runtimeConfig;
2306
- return mergeCodexThreadConfigs(runtimeConfig, CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG) ?? {
2307
- ...runtimeConfig,
2308
- ...CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG
2309
- };
2509
+ const baseConfig = buildCodexRuntimeThreadConfig(mergeCodexThreadConfigs(config, webSearchConfig), options);
2510
+ return applyCodexManagedShellEnvironment({
2511
+ ...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,
2512
+ ...params.bootstrapContextMode === "lightweight" ? CODEX_NO_PROJECT_DOCS_CONFIG : {}
2513
+ }, options.shellEnvironment, options.disableLoginShell);
2310
2514
  }
2311
2515
  function buildCodexRingZeroThreadConfigPatch(params, hostSystemAgentActive = isHostScopedAgentToolActive("openclaw"), inheritedMcpServerNames = []) {
2312
2516
  if (!hostSystemAgentActive || !isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow)) return;
2313
- return buildCodexRestrictedToolThreadConfigPatch(inheritedMcpServerNames);
2517
+ return {
2518
+ ...buildRestrictedToolConfigPatch(inheritedMcpServerNames),
2519
+ ...CODEX_NO_PROJECT_DOCS_CONFIG
2520
+ };
2314
2521
  }
2315
- function buildCodexRestrictedToolThreadConfigPatch(inheritedMcpServerNames) {
2522
+ function buildRestrictedToolConfigPatch(inheritedMcpServerNames) {
2316
2523
  const mcpServers = Object.fromEntries([...new Set(inheritedMcpServerNames)].toSorted().map((name) => [name, { enabled: false }]));
2317
2524
  return {
2318
2525
  ...CODEX_RING_ZERO_THREAD_CONFIG,
@@ -2336,12 +2543,12 @@ async function readCodexInheritedMcpServerNames(client, cwd, signal) {
2336
2543
  if (!isJsonObject(configuredServers)) throw new Error("Codex config/read returned invalid mcp_servers");
2337
2544
  return Object.keys(configuredServers).toSorted();
2338
2545
  }
2339
- async function assertCodexRestrictedToolSurfaceHasNoManagedHooks(client, signal) {
2546
+ async function assertCodexManagedRequirementsDoNotOverrideToolPolicy(client, options, signal) {
2340
2547
  const response = await client.request("configRequirements/read", void 0, { signal });
2341
2548
  if (!isJsonObject(response) || !Object.hasOwn(response, "requirements")) throw new Error("Codex configRequirements/read returned an invalid response");
2342
2549
  if (response.requirements === null) return;
2343
2550
  if (!isJsonObject(response.requirements)) throw new Error("Codex configRequirements/read returned invalid requirements");
2344
- for (const key of [
2551
+ if (options.restrictedToolSurface) for (const key of [
2345
2552
  "hooks",
2346
2553
  "managedHooks",
2347
2554
  "managed_hooks"
@@ -2351,6 +2558,7 @@ async function assertCodexRestrictedToolSurfaceHasNoManagedHooks(client, signal)
2351
2558
  if (!isJsonObject(hooks)) throw new Error("Codex configRequirements/read returned invalid managed hooks");
2352
2559
  if (hasNonEmptyJsonValue(hooks)) throw new Error("Codex restricted tool surface cannot override managed hooks");
2353
2560
  }
2561
+ const additionalDeniedFeatures = new Set(options.additionalDeniedFeatures);
2354
2562
  for (const key of ["featureRequirements", "feature_requirements"]) {
2355
2563
  const requirements = response.requirements[key];
2356
2564
  if (requirements === void 0 || requirements === null) continue;
@@ -2358,7 +2566,8 @@ async function assertCodexRestrictedToolSurfaceHasNoManagedHooks(client, signal)
2358
2566
  for (const [feature, enabled] of Object.entries(requirements)) {
2359
2567
  if (typeof enabled !== "boolean") throw new Error("Codex configRequirements/read returned invalid feature requirements");
2360
2568
  const canonicalFeature = CODEX_RING_ZERO_RESTRICTED_FEATURE_ALIASES.get(feature) ?? feature;
2361
- if (enabled && CODEX_RING_ZERO_RESTRICTED_FEATURES.has(canonicalFeature)) throw new Error(`Codex restricted tool surface cannot override required feature ${feature}`);
2569
+ const deniedByToolPolicy = options.restrictedToolSurface && CODEX_RING_ZERO_RESTRICTED_FEATURES.has(canonicalFeature) || additionalDeniedFeatures.has(canonicalFeature);
2570
+ if (enabled && deniedByToolPolicy) throw new Error(`Codex tool policy cannot override required feature ${feature}`);
2362
2571
  }
2363
2572
  }
2364
2573
  }
@@ -2408,6 +2617,14 @@ function resolveCodexThreadEnvironmentSelection(options) {
2408
2617
  }
2409
2618
  //#endregion
2410
2619
  //#region extensions/codex/src/app-server/thread-lifecycle-io.ts
2620
+ function resolveCodexThreadAgentDir(params) {
2621
+ const agentId = resolveSessionAgentIdsStrict({
2622
+ config: params.params.config,
2623
+ sessionKey: params.params.sessionKey,
2624
+ agentId: params.agentId ?? params.params.agentId
2625
+ }).sessionAgentId;
2626
+ return params.agentDir ?? params.params.agentDir ?? resolveAgentDir$1(params.params.config ?? {}, agentId);
2627
+ }
2411
2628
  function resolveCodexThreadRolloutPath(thread) {
2412
2629
  const rolloutPath = thread.path?.trim();
2413
2630
  if (!rolloutPath || !path.isAbsolute(rolloutPath) || path.extname(rolloutPath) !== ".jsonl" || !path.basename(rolloutPath).includes(thread.id)) return;
@@ -2416,6 +2633,8 @@ function resolveCodexThreadRolloutPath(thread) {
2416
2633
  async function resumeExistingCodexThread(params, context) {
2417
2634
  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;
2418
2635
  let resumeReservation;
2636
+ let resumeResponseAccepted = false;
2637
+ const abandonClient = params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client));
2419
2638
  try {
2420
2639
  const authProfileId = resumeBinding.connectionScope === "supervision" ? void 0 : params.params.authProfileId ?? resumeBinding.authProfileId;
2421
2640
  const finalConfigPatch = context.prebuiltFinalConfigPatch ?? params.buildFinalConfigPatch?.({
@@ -2429,6 +2648,7 @@ async function resumeExistingCodexThread(params, context) {
2429
2648
  const resumeConfig = applyCodexNativeSkillIsolation(mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, finalConfigPatch.configPatch), nativeSkillIsolation);
2430
2649
  const resumeParams = lifecycleTiming.measureSync("thread-resume-params", () => buildThreadResumeParams(params.params, {
2431
2650
  threadId: resumeBinding.threadId,
2651
+ cwd: params.cwd,
2432
2652
  authProfileId,
2433
2653
  model: startModelSelection.model,
2434
2654
  modelProvider: startModelProvider,
@@ -2442,29 +2662,35 @@ async function resumeExistingCodexThread(params, context) {
2442
2662
  nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
2443
2663
  webSearchAllowed: params.webSearchAllowed,
2444
2664
  hostSystemAgentActive,
2445
- restrictedToolSurfaceInheritedMcpServerNames
2665
+ restrictedToolSurfaceInheritedMcpServerNames,
2666
+ shellEnvironment: params.shellEnvironment,
2667
+ disableLoginShell: params.disableLoginShell
2446
2668
  }));
2447
2669
  const requestModelProvider = typeof resumeParams.modelProvider === "string" && resumeParams.modelProvider.trim() ? resumeParams.modelProvider : void 0;
2448
2670
  throwIfAborted();
2449
- if (resumeBinding.preserveNativeModel === true) {
2450
- const current = await lifecycleTiming.measure("thread-read-adoption-status", () => params.client.request("thread/read", {
2451
- threadId: resumeBinding.threadId,
2452
- includeTurns: false
2453
- }, { signal: params.signal }));
2454
- throwIfAborted();
2455
- if (current.thread.status?.type === "active") throw new CodexAdoptedThreadActiveError();
2456
- }
2457
2671
  resumeReservation = params.reserveResumeThread?.(resumeBinding.threadId);
2458
2672
  const response = await lifecycleTiming.measure("thread-resume-request", () => resumeCodexAppServerThread({
2459
2673
  client: params.client,
2460
- abandonClient: params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)),
2674
+ abandonClient,
2461
2675
  request: resumeParams,
2462
- signal: params.signal
2676
+ signal: params.signal,
2677
+ assertCurrent: context.assertResumeOwnership,
2678
+ isPrewriteOwnershipError: (error) => error instanceof CodexAdoptedThreadActiveError
2463
2679
  }));
2680
+ resumeResponseAccepted = true;
2681
+ assertCodexThreadAcceptsDirectInput(response.thread);
2682
+ context.assertResumeConfiguration?.();
2683
+ if (resumeBinding.pendingResumeConfiguration) await attestCodexPluginThreadApps({
2684
+ client: params.client,
2685
+ threadId: response.thread.id,
2686
+ appIds: context.prebuiltPluginThreadConfig?.provisionalAppIds ?? [],
2687
+ signal: params.signal
2688
+ });
2464
2689
  if (ringZeroActive || isMessageOnlyCodexSourceReply(params.params) || params.params.pluginHarnessToolPolicyRestricted === true) try {
2465
2690
  await lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, response.thread.id, resumeParams.config, params.signal));
2466
2691
  } catch (error) {
2467
- await (params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)))();
2692
+ context.assertResumeOwnership?.();
2693
+ await abandonClient();
2468
2694
  throw new CodexRestrictedToolSurfaceAttestationError(error);
2469
2695
  }
2470
2696
  throwIfAborted();
@@ -2472,6 +2698,7 @@ async function resumeExistingCodexThread(params, context) {
2472
2698
  const nextMcpServersFingerprint = params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : resumeBinding.mcpServersFingerprint;
2473
2699
  const resumePatch = {
2474
2700
  clientId: resolveCodexAppServerClientInstanceId(params.client),
2701
+ pendingResumeConfiguration: void 0,
2475
2702
  cwd: params.cwd,
2476
2703
  rolloutPath: resolveCodexThreadRolloutPath(response.thread) ?? resumeBinding.rolloutPath,
2477
2704
  authProfileId: boundAuthProfileId,
@@ -2502,7 +2729,7 @@ async function resumeExistingCodexThread(params, context) {
2502
2729
  kind: "patch",
2503
2730
  threadId: resumeBinding.threadId,
2504
2731
  patch: resumePatch
2505
- }))) throw new CodexThreadBindingConflictError(resumeBinding.threadId, "committing a resumed thread");
2732
+ }, context.assertResumeConfiguration))) throw new CodexThreadBindingConflictError(resumeBinding.threadId, "committing a resumed thread");
2506
2733
  if (contextEngineBinding) embeddedAgentLog.info("codex app-server wrote context-engine thread binding", {
2507
2734
  sessionId: params.params.sessionId,
2508
2735
  sessionKey: params.params.sessionKey,
@@ -2539,19 +2766,26 @@ async function resumeExistingCodexThread(params, context) {
2539
2766
  };
2540
2767
  } catch (error) {
2541
2768
  resumeReservation?.release();
2542
- if (isCodexAppServerStartSelectionChangedError(error)) throw error;
2769
+ if (!resumeResponseAccepted && (!(error instanceof CodexAppServerRpcError) || isCodexAppServerOverloadError(error))) throw error;
2543
2770
  if (error instanceof CodexRestrictedToolSurfaceAttestationError) {
2544
- await clearCurrentBinding("retiring a failed restricted-tool-surface attestation");
2771
+ if (!resumeBinding.pendingResumeConfiguration) await clearCurrentBinding("retiring a failed restricted-tool-surface attestation");
2545
2772
  throw error;
2546
2773
  }
2547
- if (error instanceof CodexAdoptedThreadActiveError) throw error;
2548
- if (isCodexAppServerUnsafeSubscriptionError(error)) throw error;
2549
- const resumeRejected = error instanceof CodexAppServerRpcError;
2550
- if (!await unsubscribeCodexThreadBestEffort(params.client, {
2551
- threadId: resumeBinding.threadId,
2552
- timeoutMs: 5e3
2553
- }) && !resumeRejected && !isCodexAppServerConnectionClosedError(error) && !params.signal?.aborted) throw new CodexAppServerUnsafeSubscriptionError("Codex thread/resume subscription cleanup failed", { cause: error });
2554
- if (isCodexAppServerConnectionClosedError(error) || params.signal?.aborted) throw error;
2774
+ if (resumeResponseAccepted) {
2775
+ if (!await unsubscribeCodexThreadBestEffort(params.client, {
2776
+ threadId: resumeBinding.threadId,
2777
+ timeoutMs: 5e3,
2778
+ assertCurrent: context.assertResumeOwnership
2779
+ }).catch(() => false)) {
2780
+ try {
2781
+ await abandonClient();
2782
+ } catch (abandonError) {
2783
+ throw new CodexAppServerUnsafeSubscriptionError("Codex thread/resume client could not be retired", { cause: abandonError });
2784
+ }
2785
+ throw new CodexAppServerUnsafeSubscriptionError("Codex thread/resume subscription cleanup failed", { cause: error });
2786
+ }
2787
+ }
2788
+ if (resumeBinding.pendingResumeConfiguration || error instanceof CodexThreadDirectInputError || params.signal?.aborted) throw error;
2555
2789
  embeddedAgentLog.warn("codex app-server thread resume failed; starting a new thread", { error });
2556
2790
  await clearCurrentBinding("rotating a stale thread binding");
2557
2791
  }
@@ -2579,7 +2813,9 @@ async function startFreshCodexThread(params, context) {
2579
2813
  model: startModelSelection.model,
2580
2814
  modelProvider: startModelProvider,
2581
2815
  hostSystemAgentActive,
2582
- restrictedToolSurfaceInheritedMcpServerNames
2816
+ restrictedToolSurfaceInheritedMcpServerNames,
2817
+ shellEnvironment: params.shellEnvironment,
2818
+ disableLoginShell: params.disableLoginShell
2583
2819
  }));
2584
2820
  const requestModelProvider = typeof startParams.modelProvider === "string" && startParams.modelProvider.trim() ? startParams.modelProvider : void 0;
2585
2821
  const response = assertCodexThreadStartResponse(await lifecycleTiming.measure("thread-start-request", async () => {
@@ -2647,6 +2883,7 @@ async function startFreshCodexThread(params, context) {
2647
2883
  cwd: params.cwd,
2648
2884
  ...rolloutPath ? { rolloutPath } : {},
2649
2885
  authProfileId: params.params.authProfileId,
2886
+ agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
2650
2887
  model: response.model ?? startParams.model ?? params.params.modelId,
2651
2888
  modelProvider: bindingModelProvider,
2652
2889
  dynamicToolsFingerprint,
@@ -2679,6 +2916,12 @@ async function startFreshCodexThread(params, context) {
2679
2916
  throw new CodexAppServerUnsafeSubscriptionError("Codex successor cleanup failed after a binding replacement conflict", cause === void 0 ? void 0 : { cause });
2680
2917
  }
2681
2918
  };
2919
+ const managedSourceHomeId = codexCatalogHomeId(resolveCodexAppServerLocalHomeDir(params.appServer.start, resolveCodexThreadAgentDir(params)));
2920
+ await lifecycleTiming.measure("thread-start-mark-managed", () => markStartedCodexManagedThread(params.bindingStore.managedThreads, {
2921
+ sourceHomeId: managedSourceHomeId,
2922
+ threadId: response.thread.id,
2923
+ ...rolloutPath ? { rolloutPath } : {}
2924
+ }));
2682
2925
  let committed;
2683
2926
  try {
2684
2927
  committed = await lifecycleTiming.measure("thread-start-write-binding", () => params.bindingStore.mutate(bindingIdentity, replacementPredecessor ? {
@@ -2716,241 +2959,54 @@ async function startFreshCodexThread(params, context) {
2716
2959
  threadId: response.thread.id,
2717
2960
  action: rotatedContextEngineBinding ? "rotated" : "started"
2718
2961
  });
2719
- return {
2720
- threadId: response.thread.id,
2721
- ...clientId ? { clientId } : {},
2722
- cwd: params.cwd,
2723
- ...rolloutPath ? { rolloutPath } : {},
2724
- authProfileId: params.params.authProfileId,
2725
- model: response.model ?? startParams.model ?? params.params.modelId,
2726
- modelProvider: response.modelProvider ?? requestModelProvider ?? startModelProvider ?? modelProvider,
2727
- dynamicToolsFingerprint,
2728
- dynamicToolsContainDeferred,
2729
- nativeSkillIsolationFingerprint,
2730
- userMcpServersFingerprint,
2731
- mcpServersFingerprint: nextMcpServersFingerprint,
2732
- configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
2733
- ringZeroConfigFingerprint,
2734
- ringZeroClientInstanceId,
2735
- networkProxyProfileName: params.appServer.networkProxy?.profileName,
2736
- networkProxyConfigFingerprint,
2737
- nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
2738
- appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
2739
- pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
2740
- pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
2741
- pluginAppPolicyContext: pluginThreadConfig?.policyContext,
2742
- contextEngine: contextEngineBinding,
2743
- environmentSelectionFingerprint,
2744
- ...!preserveExistingBinding ? { liveThreadConfigFingerprint: fingerprintCodexThreadConfig({
2745
- ...startParams,
2746
- model: response.model ?? startParams.model ?? null,
2747
- requestedModel: startParams.model ?? null,
2748
- modelProvider: bindingModelProvider ?? null,
2749
- requestedModelProvider: startParams.modelProvider ?? bindingModelProvider ?? null
2750
- }, params.params.authProfileId, dynamicToolsFingerprint) } : {},
2751
- lifecycle: {
2752
- action: "started",
2753
- ...rotatedContextEngineBinding ? { rotatedContextEngineBinding } : {}
2754
- }
2755
- };
2962
+ return buildStartedCodexThreadBinding({
2963
+ bindingModelProvider,
2964
+ clientId,
2965
+ context,
2966
+ finalConfigPatch,
2967
+ nextMcpServersFingerprint,
2968
+ params,
2969
+ pluginThreadConfig,
2970
+ response,
2971
+ rolloutPath,
2972
+ startModelProvider: requestModelProvider ?? startModelProvider,
2973
+ startParams,
2974
+ modelProvider
2975
+ });
2756
2976
  }
2757
2977
  //#endregion
2758
- //#region extensions/codex/src/app-server/profiler-flag.ts
2759
- const PROFILER_FLAGS = ["profiler", "codex.profiler"];
2760
- /** Checks the generic and Codex-specific profiler diagnostic flags. */
2761
- function isCodexAppServerProfilerEnabled(config, env = process.env) {
2762
- return PROFILER_FLAGS.some((flag) => isDiagnosticFlagEnabled(flag, config, env));
2978
+ //#region extensions/codex/src/app-server/thread-lifecycle-warm.ts
2979
+ /** Preserves the caller's abort reason across thread ownership transitions. */
2980
+ function throwIfCodexThreadLifecycleAborted(signal) {
2981
+ if (!signal?.aborted) return;
2982
+ const reason = signal.reason;
2983
+ if (reason instanceof Error) throw reason;
2984
+ const error = new Error(typeof reason === "string" && reason.length > 0 ? reason : "codex app-server thread lifecycle aborted");
2985
+ error.name = "AbortError";
2986
+ throw error;
2763
2987
  }
2764
- //#endregion
2765
- //#region extensions/codex/src/app-server/thread-lifecycle-timing.ts
2766
- const CODEX_THREAD_LIFECYCLE_TIMING_WARN_TOTAL_MS = 1e3;
2767
- const CODEX_THREAD_LIFECYCLE_TIMING_WARN_STAGE_MS = 500;
2768
- function shouldWarnCodexThreadLifecycleTimingSummary(summary, options = {}) {
2769
- const totalThresholdMs = options.totalThresholdMs ?? CODEX_THREAD_LIFECYCLE_TIMING_WARN_TOTAL_MS;
2770
- const stageThresholdMs = options.stageThresholdMs ?? CODEX_THREAD_LIFECYCLE_TIMING_WARN_STAGE_MS;
2771
- return summary.totalMs >= totalThresholdMs || summary.spans.some((span) => span.durationMs >= stageThresholdMs);
2988
+ /** Releases consumed subscription ownership or retires an unsafe client. */
2989
+ async function releaseCodexConsumedLiveThread(options) {
2990
+ if (await options.lifecycleTiming.measure("retained-thread-unsubscribe", () => unsubscribeCodexThreadBestEffort(options.client, {
2991
+ threadId: options.threadId,
2992
+ timeoutMs: 5e3,
2993
+ assertCurrent: options.assertCurrent
2994
+ }))) return;
2995
+ return await abandonCodexLiveThreadRelease(options, options.cause);
2772
2996
  }
2773
- function formatCodexThreadLifecycleTimingSummary(params) {
2774
- const spans = params.summary.spans.length > 0 ? params.summary.spans.map((span) => `${span.name}:${span.durationMs}ms@${span.elapsedMs}ms`).join(",") : "none";
2775
- return `[trace:codex-app-server] thread lifecycle: runId=${params.runId} sessionId=${params.sessionId} sessionKey=${params.sessionKey ?? "unknown"} action=${params.action} totalMs=${params.summary.totalMs} stages=${spans}`;
2997
+ async function abandonCodexLiveThreadRelease(options, cause) {
2998
+ options.assertCurrent?.();
2999
+ await (options.abandonClient ?? (() => closeCodexStartupClientBestEffort(options.client)))();
3000
+ throw new CodexAppServerUnsafeSubscriptionError(`Codex retained thread subscription could not be released: ${options.threadId}`, cause !== void 0 ? { cause } : void 0);
2776
3001
  }
2777
- function createCodexThreadLifecycleTimingTracker(options = {}) {
2778
- const log = options.log ?? embeddedAgentLog;
2779
- if (!options.enabled && log.isEnabled?.("trace") !== true) return {
2780
- async measure(_name, run) {
2781
- return await run();
2782
- },
2783
- measureSync(_name, run) {
2784
- return run();
2785
- },
2786
- mark() {},
2787
- logSummary() {}
2788
- };
2789
- const now = options.now ?? Date.now;
2790
- const startedAt = now();
2791
- let didLog = false;
2792
- const spans = [];
2793
- const toMs = (value) => Math.max(0, Math.round(value));
2794
- const record = (name, spanStartedAt) => {
2795
- const currentAt = now();
2796
- spans.push({
2797
- name,
2798
- durationMs: toMs(currentAt - spanStartedAt),
2799
- elapsedMs: toMs(currentAt - startedAt)
2800
- });
2801
- };
2802
- const snapshot = () => ({
2803
- totalMs: toMs(now() - startedAt),
2804
- spans: spans.slice()
2805
- });
2806
- return {
2807
- async measure(name, run) {
2808
- const spanStartedAt = now();
2809
- try {
2810
- return await run();
2811
- } finally {
2812
- record(name, spanStartedAt);
2813
- }
2814
- },
2815
- measureSync(name, run) {
2816
- const spanStartedAt = now();
2817
- try {
2818
- return run();
2819
- } finally {
2820
- record(name, spanStartedAt);
2821
- }
2822
- },
2823
- mark(name) {
2824
- record(name, now());
2825
- },
2826
- logSummary(params) {
2827
- if (didLog) return;
2828
- const summary = snapshot();
2829
- const shouldWarn = shouldWarnCodexThreadLifecycleTimingSummary(summary, options);
2830
- if (!shouldWarn && !log.isEnabled?.("trace")) return;
2831
- didLog = true;
2832
- const message = formatCodexThreadLifecycleTimingSummary({
2833
- runId: params.runId,
2834
- sessionId: params.sessionId,
2835
- sessionKey: params.sessionKey,
2836
- action: params.action,
2837
- summary
2838
- });
2839
- const meta = {
2840
- runId: params.runId,
2841
- sessionId: params.sessionId,
2842
- sessionKey: params.sessionKey,
2843
- action: params.action,
2844
- threadId: params.threadId,
2845
- totalMs: summary.totalMs,
2846
- spans: summary.spans
2847
- };
2848
- if (shouldWarn) log.warn(message, meta);
2849
- else log.trace(message, meta);
2850
- }
2851
- };
2852
- }
2853
- //#endregion
2854
- //#region extensions/codex/src/app-server/thread-lifecycle-preflight.ts
2855
- async function prepareCodexThreadLifecyclePreflight(params) {
2856
- const lifecycleTiming = createCodexThreadLifecycleTimingTracker({
2857
- ...params.timing,
2858
- enabled: params.timing?.enabled ?? isCodexAppServerProfilerEnabled(params.params.config)
2859
- });
2860
- const legacyDynamicToolsFingerprint = lifecycleTiming.measureSync("legacy-dynamic-tools-fingerprint", () => codexLegacyDynamicToolsFingerprint(params.dynamicTools));
2861
- const dynamicToolsFingerprint = lifecycleTiming.measureSync("dynamic-tools-fingerprint", () => hashCodexAppServerBindingFingerprint(legacyDynamicToolsFingerprint));
2862
- const dynamicToolsContainDeferred = flattenCodexDynamicToolFunctions(params.dynamicTools).some((tool) => tool.deferLoading === true);
2863
- const webSearchThreadConfigFingerprint = fingerprintJsonObject(lifecycleTiming.measureSync("web-search-plan", () => resolveCodexWebSearchPlan({
2864
- config: params.params.config,
2865
- disableTools: params.params.disableTools,
2866
- nativeToolSurfaceEnabled: params.nativeCodeModeEnabled,
2867
- nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
2868
- webSearchAllowed: params.webSearchAllowed
2869
- })).threadConfig);
2870
- const networkProxyConfigFingerprint = params.appServer.networkProxy?.configFingerprint;
2871
- const contextEngineBinding = lifecycleTiming.measureSync("context-engine-binding", () => buildContextEngineBinding(params.params, params.contextEngineProjection));
2872
- const userMcpServersConfigPatch = params.userMcpServersEnabled === false ? void 0 : await buildCodexUserMcpServersThreadConfigPatchForRuntime(params.params.config, {
2873
- agentId: params.agentId ?? params.params.agentId,
2874
- agentDir: params.params.agentDir,
2875
- allowLiteralOAuthProjection: params.appServer.connectionClass !== "remote",
2876
- toolOverrides: params.params.toolOverrides,
2877
- onServerUnavailable: (serverName, error) => embeddedAgentLog.warn("skipping unavailable MCP OAuth server", {
2878
- serverName,
2879
- error: formatErrorMessage(error)
2880
- })
2881
- });
2882
- const nativeSkillIsolation = await lifecycleTiming.measure("native-skill-isolation", () => resolveCodexNativeSkillIsolation({
2883
- client: params.client,
2884
- codexHome: params.appServer.start.env?.CODEX_HOME,
2885
- cwd: params.cwd,
2886
- home: params.appServer.start.env?.HOME,
2887
- signal: params.signal,
2888
- userProfile: params.appServer.start.env?.USERPROFILE
2889
- }));
2890
- const nativeSkillIsolationFingerprint = nativeSkillIsolation ? fingerprintJsonObject({
2891
- version: 1,
2892
- disabledUserSkillPaths: nativeSkillIsolation.disabledUserSkillPaths
2893
- }) : void 0;
2894
- const legacyUserMcpServersFingerprint = legacyFingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
2895
- const userMcpServersFingerprint = fingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
2896
- const environmentSelectionFingerprint = fingerprintEnvironmentSelection(params.environmentSelection);
2897
- const hostSystemAgentActive = params.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw");
2898
- const ringZeroActive = hostSystemAgentActive && isSystemAgentOnlyCodexDynamicToolAllowlist(params.params.toolsAllow);
2899
- const messageOnlySourceReply = isMessageOnlyCodexSourceReply(params.params);
2900
- const restrictedToolSurface = ringZeroActive || messageOnlySourceReply || params.params.pluginHarnessToolPolicyRestricted === true;
2901
- if (restrictedToolSurface && params.nativeCodeModeEnabled !== false) throw new Error("Codex restricted tool surfaces require native code mode to be disabled");
2902
- const restrictedToolSurfaceInheritedMcpServerNames = restrictedToolSurface ? await lifecycleTiming.measure("restricted-tool-surface-mcp-config-read", () => readCodexInheritedMcpServerNames(params.client, params.cwd, params.signal)) : [];
2903
- if (restrictedToolSurface) await lifecycleTiming.measure("restricted-tool-surface-config-requirements-read", () => assertCodexRestrictedToolSurfaceHasNoManagedHooks(params.client, params.signal));
2904
- const ringZeroConfigFingerprint = ringZeroActive ? fingerprintJsonObject({
2905
- version: 1,
2906
- baseInstructions: "",
2907
- config: buildCodexRingZeroThreadConfigPatch(params.params, true, restrictedToolSurfaceInheritedMcpServerNames)
2908
- }) : void 0;
2909
- return {
2910
- contextEngineBinding,
2911
- dynamicToolsContainDeferred,
2912
- dynamicToolsFingerprint,
2913
- environmentSelectionFingerprint,
2914
- hostSystemAgentActive,
2915
- legacyDynamicToolsFingerprint,
2916
- legacyUserMcpServersFingerprint,
2917
- lifecycleTiming,
2918
- nativeSkillIsolation,
2919
- nativeSkillIsolationFingerprint,
2920
- networkProxyConfigFingerprint,
2921
- ringZeroActive,
2922
- ringZeroClientInstanceId: ringZeroActive ? getCodexAppServerClientInstanceId(params.client) : void 0,
2923
- ringZeroConfigFingerprint,
2924
- restrictedToolSurface,
2925
- restrictedToolSurfaceInheritedMcpServerNames,
2926
- userMcpServersConfigPatch,
2927
- userMcpServersFingerprint,
2928
- webSearchThreadConfigFingerprint
2929
- };
2930
- }
2931
- //#endregion
2932
- //#region extensions/codex/src/app-server/thread-lifecycle-warm.ts
2933
- /** Preserves the caller's abort reason across thread ownership transitions. */
2934
- function throwIfCodexThreadLifecycleAborted(signal) {
2935
- if (!signal?.aborted) return;
2936
- const reason = signal.reason;
2937
- if (reason instanceof Error) throw reason;
2938
- const error = new Error(typeof reason === "string" && reason.length > 0 ? reason : "codex app-server thread lifecycle aborted");
2939
- error.name = "AbortError";
2940
- throw error;
2941
- }
2942
- /** Releases consumed subscription ownership or retires an unsafe client. */
2943
- async function releaseCodexConsumedLiveThread(options) {
2944
- if (await options.lifecycleTiming.measure("retained-thread-unsubscribe", () => unsubscribeCodexThreadBestEffort(options.client, {
2945
- threadId: options.threadId,
2946
- timeoutMs: 5e3
2947
- }))) return;
2948
- await (options.abandonClient ?? (() => closeCodexStartupClientBestEffort(options.client)))();
2949
- throw new CodexAppServerUnsafeSubscriptionError(`Codex retained thread subscription could not be released: ${options.threadId}`, options.cause !== void 0 ? { cause: options.cause } : void 0);
2950
- }
2951
- /** Transfers retained-slot ownership before unconditionally releasing it. */
2952
- async function releaseCodexRetainedLiveThread(options) {
2953
- if (await consumeCodexAppServerLiveThread(options.client, options.threadId)) await releaseCodexConsumedLiveThread(options);
3002
+ /** Releases through the retained owner, preserving its guarded callback and rollback. */
3003
+ async function releaseCodexRetainedLiveThread(options) {
3004
+ try {
3005
+ return await options.lifecycleTiming.measure("retained-thread-unsubscribe", () => releaseCodexAppServerLiveThread(options.client, options.threadId, options.assertCurrent));
3006
+ } catch (error) {
3007
+ if (isCodexAppServerUnsafeSubscriptionError(error)) throw error;
3008
+ return await abandonCodexLiveThreadRelease(options, error);
3009
+ }
2954
3010
  }
2955
3011
  /** Reuses one safely owned, fully matching subscription on its original client. */
2956
3012
  async function tryReuseCodexLiveThread(options) {
@@ -2968,6 +3024,7 @@ async function tryReuseCodexLiveThread(options) {
2968
3024
  const resumeConfig = mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, prebuiltFinalConfigPatch.configPatch);
2969
3025
  const resumeParams = lifecycleTiming.measureSync("warm-thread-resume-params", () => buildThreadResumeParams(params.params, {
2970
3026
  threadId: binding.threadId,
3027
+ cwd: params.cwd,
2971
3028
  authProfileId: resumeAuthProfileId,
2972
3029
  model: startModelSelection.model,
2973
3030
  modelProvider: startModelProvider,
@@ -2981,7 +3038,9 @@ async function tryReuseCodexLiveThread(options) {
2981
3038
  nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
2982
3039
  webSearchAllowed: params.webSearchAllowed,
2983
3040
  hostSystemAgentActive,
2984
- restrictedToolSurfaceInheritedMcpServerNames
3041
+ restrictedToolSurfaceInheritedMcpServerNames,
3042
+ shellEnvironment: params.shellEnvironment,
3043
+ disableLoginShell: params.disableLoginShell
2985
3044
  }));
2986
3045
  const liveThreadConfigFingerprint = fingerprintCodexThreadConfig({
2987
3046
  ...resumeParams,
@@ -3038,6 +3097,667 @@ async function tryReuseCodexLiveThread(options) {
3038
3097
  }
3039
3098
  }
3040
3099
  //#endregion
3100
+ //#region extensions/codex/src/app-server/thread-lifecycle-adoption.ts
3101
+ /** Passive refusal must precede releasing or acquiring any native subscription. */
3102
+ async function assertAdoptedCodexThreadResumeAllowed(params, threadId, context) {
3103
+ const { thread } = await context.lifecycleTiming.measure("thread-read-adoption-status", () => params.client.request("thread/read", {
3104
+ threadId,
3105
+ includeTurns: false
3106
+ }, { signal: params.signal }));
3107
+ context.throwIfAborted();
3108
+ assertCodexThreadAcceptsDirectInput(thread);
3109
+ if (thread.status?.type === "active") throw new CodexAdoptedThreadActiveError();
3110
+ }
3111
+ /** Preserve attach's native-queue-before-binding-lease order when consuming pending intent. */
3112
+ async function withCodexThreadLifecycleBinding(params, run) {
3113
+ const identity = sessionBindingIdentity({
3114
+ sessionId: params.params.sessionId,
3115
+ sessionKey: params.params.sessionKey,
3116
+ agentId: params.agentId ?? params.params.agentId,
3117
+ config: params.params.config
3118
+ });
3119
+ const snapshot = await params.bindingStore.read(identity);
3120
+ const pendingThreadId = snapshot?.pendingResumeConfiguration ? snapshot.threadId : void 0;
3121
+ const runWithLease = () => params.bindingStore.withLease(identity, async () => {
3122
+ const binding = await params.bindingStore.read(identity);
3123
+ if (pendingThreadId && (binding?.threadId !== pendingThreadId || !binding.pendingResumeConfiguration)) throw new CodexThreadBindingConflictError(pendingThreadId, "acquiring a pending resume configuration");
3124
+ if (!pendingThreadId && binding?.pendingResumeConfiguration) throw new CodexThreadBindingConflictError(binding.threadId, "acquiring a pending resume configuration");
3125
+ return await run(identity, binding);
3126
+ });
3127
+ return pendingThreadId ? await withExclusiveCodexAppServerThread({
3128
+ bindingStore: params.bindingStore,
3129
+ identity,
3130
+ threadId: pendingThreadId,
3131
+ run: runWithLease
3132
+ }) : await runWithLease();
3133
+ }
3134
+ /** Completes manual attachment only under the native queue and exact binding lease. */
3135
+ async function resumePendingCodexThread(params, context) {
3136
+ const { binding, contextEngineBinding, lifecycleTiming, restrictedToolSurface } = context;
3137
+ if (isIncognitoSessionKey(params.params.sessionKey) || context.transientRestriction || !restrictedToolSurface && binding.nativeToolPolicyRestricted === true || (contextEngineBinding ? !isContextEngineBindingCompatible(binding.contextEngine, contextEngineBinding) : binding.contextEngine !== void 0) || shouldRotateCodexGpt56MultiAgentBinding({
3138
+ bindingModel: binding.model,
3139
+ requestedModel: params.params.modelId
3140
+ })) 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.`);
3141
+ const prebuiltPluginThreadConfig = params.pluginThreadConfig?.enabled ? await lifecycleTiming.measure("plugin-config-build", () => params.pluginThreadConfig?.build()) : void 0;
3142
+ const clientId = resolveCodexAppServerClientInstanceId(params.client);
3143
+ const configuration = await preparePendingCodexThreadResume(params, binding, context.dynamicToolsFingerprint, async (assertCurrent) => {
3144
+ const released = await context.releaseRetainedThread(binding.threadId, assertCurrent);
3145
+ assertCurrent();
3146
+ if (!released || binding.clientId && binding.clientId !== clientId) await releaseCodexConsumedLiveThread({
3147
+ client: params.client,
3148
+ abandonClient: params.abandonClient,
3149
+ lifecycleTiming,
3150
+ threadId: binding.threadId,
3151
+ assertCurrent
3152
+ });
3153
+ });
3154
+ try {
3155
+ const resumed = await resumeExistingCodexThread(params, {
3156
+ ...context,
3157
+ prebuiltPluginThreadConfig,
3158
+ assertResumeConfiguration: configuration.assertConfigured,
3159
+ assertResumeOwnership: configuration.assertCurrent
3160
+ });
3161
+ if (!resumed) throw new Error(`Codex did not configure resumed thread ${binding.threadId}.`);
3162
+ return resumed;
3163
+ } finally {
3164
+ configuration.dispose();
3165
+ }
3166
+ }
3167
+ /** Manual attachment is intent, never evidence that loaded native overrides took effect. */
3168
+ async function preparePendingCodexThreadResume(params, binding, dynamicToolsFingerprint, releaseSubscription) {
3169
+ 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.`);
3170
+ const agentDir = resolveCodexThreadAgentDir(params);
3171
+ const localHome = resolveCodexAppServerLocalHomeDir(params.appServer.start, agentDir);
3172
+ 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");
3173
+ if (isCodexAppServerLiveThreadClaimed(params.client, binding.threadId)) throw fail("the thread is claimed by active work; stop that run before resuming");
3174
+ const assertCurrent = captureExclusiveSharedCodexAppServerClient(params.client);
3175
+ const { thread } = await params.client.request("thread/read", {
3176
+ threadId: binding.threadId,
3177
+ includeTurns: false
3178
+ }, { signal: params.signal });
3179
+ assertCurrent();
3180
+ const statusType = thread.status?.type;
3181
+ if (thread.id !== binding.threadId || statusType !== "idle" && statusType !== "notLoaded") throw fail("the native thread is not idle; wait for its current run to finish");
3182
+ assertCodexThreadAcceptsDirectInput(thread);
3183
+ let unloaded = statusType === "notLoaded";
3184
+ const dispose = params.client.addNotificationHandler((notification) => {
3185
+ 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;
3186
+ });
3187
+ try {
3188
+ const rolloutPath = thread.path ?? binding.rolloutPath;
3189
+ const metadata = rolloutPath ? await readCodexSessionMeta(path.join(localHome, "sessions"), rolloutPath, binding.threadId) : void 0;
3190
+ if (!metadata) throw fail("its native tool catalog could not be read from the selected Codex home");
3191
+ const recordedTools = metadata.dynamic_tools ?? [];
3192
+ if (!Array.isArray(recordedTools) || codexDynamicToolsFingerprint(recordedTools) !== dynamicToolsFingerprint) throw fail("its immutable native tool catalog does not match the current OpenClaw tools");
3193
+ assertCurrent();
3194
+ await releaseSubscription(assertCurrent);
3195
+ assertCurrent();
3196
+ return {
3197
+ assertConfigured: () => {
3198
+ assertCurrent();
3199
+ if (!unloaded) throw fail("Codex did not confirm unloading its previous configuration");
3200
+ },
3201
+ assertCurrent,
3202
+ dispose
3203
+ };
3204
+ } catch (error) {
3205
+ dispose();
3206
+ throw error;
3207
+ }
3208
+ }
3209
+ //#endregion
3210
+ //#region extensions/codex/src/app-server/native-hook-relay-state.ts
3211
+ const pending = /* @__PURE__ */ new Set();
3212
+ /** Owns delayed hook-relay cleanup across runtime scheduling and test teardown. */
3213
+ const nativeHookRelayUnregisterQueue = {
3214
+ add(entry) {
3215
+ pending.add(entry);
3216
+ },
3217
+ delete(entry) {
3218
+ return pending.delete(entry);
3219
+ },
3220
+ flush() {
3221
+ while (pending.size > 0) {
3222
+ const entry = pending.values().next().value;
3223
+ if (!entry) return;
3224
+ clearTimeout(entry.timeout);
3225
+ entry.unregister();
3226
+ }
3227
+ },
3228
+ clear() {
3229
+ for (const entry of pending) clearTimeout(entry.timeout);
3230
+ pending.clear();
3231
+ }
3232
+ };
3233
+ //#endregion
3234
+ //#region extensions/codex/src/app-server/native-hook-relay.ts
3235
+ /**
3236
+ * Bridges Codex native hook callbacks into OpenClaw's native hook relay so
3237
+ * app-server tool events can still run OpenClaw policy and diagnostics.
3238
+ */
3239
+ /** Codex hook events that can be registered through OpenClaw's native relay. */
3240
+ const CODEX_NATIVE_HOOK_RELAY_EVENTS = [
3241
+ "pre_tool_use",
3242
+ "post_tool_use",
3243
+ "permission_request",
3244
+ "before_agent_finalize"
3245
+ ];
3246
+ const CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS = CODEX_NATIVE_HOOK_RELAY_EVENTS.filter((event) => event !== "permission_request");
3247
+ const CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS = 30 * 6e4;
3248
+ /** Extra relay lifetime after the expected turn budget, preventing late hook drops. */
3249
+ const CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS = 5 * 6e4;
3250
+ const CODEX_NATIVE_HOOK_RELAY_COMMAND_MIN_PARENT_MARGIN_MS = 250;
3251
+ const CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS = 1e3;
3252
+ const CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC = 10;
3253
+ const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS = 1e4;
3254
+ const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS = 5e3;
3255
+ const MAX_PENDING_DIRECT_CHILD_ADMISSIONS = 32;
3256
+ const nativeHookPolicyByClient = /* @__PURE__ */ new WeakMap();
3257
+ const CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID = {
3258
+ exec: [
3259
+ "Bash",
3260
+ "exec",
3261
+ "exec_command"
3262
+ ],
3263
+ apply_patch: [
3264
+ "apply_patch",
3265
+ "Write",
3266
+ "Edit"
3267
+ ],
3268
+ spawn_agent: ["spawn_agent", "Agent"]
3269
+ };
3270
+ /** Enterprise managed-only policy silently drops the session-layer hooks that enforce OpenClaw. */
3271
+ async function assertCodexNativeHookRelayAllowed(client, signal) {
3272
+ let attestation = nativeHookPolicyByClient.get(client);
3273
+ if (!attestation) {
3274
+ attestation = client.request("configRequirements/read", void 0, { signal }).then((response) => {
3275
+ if (!isJsonObject(response) || !Object.hasOwn(response, "requirements")) throw new Error("Codex configRequirements/read returned an invalid hook policy response");
3276
+ const requirements = response.requirements;
3277
+ if (requirements === null) return;
3278
+ if (!isJsonObject(requirements)) throw new Error("Codex configRequirements/read returned invalid hook policy requirements");
3279
+ const managedOnly = requirements.allowManagedHooksOnly;
3280
+ if (managedOnly !== void 0 && managedOnly !== null && typeof managedOnly !== "boolean") throw new Error("Codex configRequirements/read returned invalid managed-only hook policy");
3281
+ if (managedOnly === true) throw new Error("Codex managed-only hooks disable the OpenClaw native hook relay; refusing unenforced execution");
3282
+ });
3283
+ nativeHookPolicyByClient.set(client, attestation);
3284
+ attestation.catch(() => {
3285
+ if (nativeHookPolicyByClient.get(client) === attestation) nativeHookPolicyByClient.delete(client);
3286
+ });
3287
+ }
3288
+ await attestation;
3289
+ }
3290
+ /** Defers relay unregister so late native hook subprocesses can still resolve. */
3291
+ function scheduleCodexNativeHookRelayUnregister(params) {
3292
+ let pending;
3293
+ const unregister = () => {
3294
+ if (!pending) return;
3295
+ const current = pending;
3296
+ pending = void 0;
3297
+ if (!nativeHookRelayUnregisterQueue.delete(current)) return;
3298
+ params.relay.unregister();
3299
+ };
3300
+ const timeout = setTimeout(unregister, resolveCodexNativeHookRelayUnregisterGraceMs(params.hookTimeoutSec));
3301
+ pending = {
3302
+ timeout,
3303
+ unregister
3304
+ };
3305
+ nativeHookRelayUnregisterQueue.add(pending);
3306
+ timeout.unref();
3307
+ }
3308
+ /** Computes the delayed unregister window from Codex's hook timeout. */
3309
+ function resolveCodexNativeHookRelayUnregisterGraceMs(hookTimeoutSec) {
3310
+ const hookTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 0;
3311
+ return Math.max(CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS, addTimerTimeoutGraceMs(hookTimeoutMs, CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS) ?? 0);
3312
+ }
3313
+ /** Records a native pre-tool failure that Codex does not project as a tool item. */
3314
+ function emitCodexNativePreToolUseFailureDiagnostic(params) {
3315
+ emitTrustedDiagnosticEvent({
3316
+ type: "tool.execution.error",
3317
+ ...params.agentId ? { agentId: params.agentId } : {},
3318
+ sessionId: params.sessionId,
3319
+ ...params.sessionKey ? { sessionKey: params.sessionKey } : {},
3320
+ runId: params.runId,
3321
+ toolName: params.failure.toolName,
3322
+ toolCallId: params.failure.toolCallId,
3323
+ durationMs: params.failure.durationMs,
3324
+ errorCategory: "before_tool_call",
3325
+ terminalReason: params.terminalReason ?? (params.signal?.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : params.failure.disposition),
3326
+ ...params.sourceTimestampMs !== void 0 ? { sourceTimestampMs: params.sourceTimestampMs } : {}
3327
+ });
3328
+ }
3329
+ /** Registers an OpenClaw native hook relay for a Codex app-server turn. */
3330
+ function createCodexNativeHookRelay(params) {
3331
+ if (params.options?.enabled === false) return;
3332
+ const directChildClaims = /* @__PURE__ */ new Map();
3333
+ const pendingDirectChildAdmissions = /* @__PURE__ */ new Map();
3334
+ let foregroundClosed = false;
3335
+ let successfulYieldRetentionAuthorized = false;
3336
+ const assertClaim = (threadId, claim) => () => directChildClaims.get(threadId) === claim;
3337
+ const rejectPendingAdmissions = (reason) => {
3338
+ for (const pending of pendingDirectChildAdmissions.values()) pending.reject(new Error(reason));
3339
+ pendingDirectChildAdmissions.clear();
3340
+ };
3341
+ const relay = registerRetainedNativeHookRelayForBundledRuntime({
3342
+ provider: "codex",
3343
+ relayId: buildCodexNativeHookRelayId({
3344
+ agentId: params.agentId,
3345
+ sessionId: params.sessionId,
3346
+ sessionKey: params.sessionKey
3347
+ }),
3348
+ ...params.generation ? { generation: params.generation } : {},
3349
+ ...params.generationMismatchGraceMs ? { generationMismatchGraceMs: params.generationMismatchGraceMs } : {},
3350
+ ...params.agentId ? { agentId: params.agentId } : {},
3351
+ sessionId: params.sessionId,
3352
+ ...params.sessionKey ? { sessionKey: params.sessionKey } : {},
3353
+ ...params.config ? { config: params.config } : {},
3354
+ runId: params.runId,
3355
+ ...params.channelId ? { channelId: params.channelId } : {},
3356
+ ...params.requester ? { requester: params.requester } : {},
3357
+ ...params.approvalContext ? { approvalContext: params.approvalContext } : {},
3358
+ allowedEvents: params.events,
3359
+ preToolUseLoopDetection: params.loopDetectionPreToolUseRelay,
3360
+ ttlMs: resolveCodexNativeHookRelayTtlMs({
3361
+ explicitTtlMs: params.options?.ttlMs,
3362
+ attemptTimeoutMs: params.attemptTimeoutMs,
3363
+ startupTimeoutMs: params.startupTimeoutMs,
3364
+ turnStartTimeoutMs: params.turnStartTimeoutMs
3365
+ }),
3366
+ signal: params.signal,
3367
+ runBeforeToolCall: params.hostCapabilities.runBeforeToolCall,
3368
+ assertActive: params.hostCapabilities.assertActive,
3369
+ retention: {
3370
+ readClaim: readCodexNativeChildThreadId,
3371
+ shouldRetainAfterForegroundClose: () => successfulYieldRetentionAuthorized && directChildClaims.size > 0,
3372
+ allowPreToolUse: (childThreadId) => directChildClaims.has(childThreadId),
3373
+ awaitForegroundAdmission: (childThreadId) => {
3374
+ if (foregroundClosed) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission unavailable"));
3375
+ const existingClaim = directChildClaims.get(childThreadId);
3376
+ if (existingClaim) return Promise.resolve(assertClaim(childThreadId, existingClaim));
3377
+ const existingPending = pendingDirectChildAdmissions.get(childThreadId);
3378
+ if (existingPending) return existingPending.promise.then((claim) => assertClaim(childThreadId, claim));
3379
+ if (pendingDirectChildAdmissions.size >= MAX_PENDING_DIRECT_CHILD_ADMISSIONS) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission capacity reached"));
3380
+ const { promise, resolve, reject } = createDeferred();
3381
+ pendingDirectChildAdmissions.set(childThreadId, {
3382
+ promise,
3383
+ resolve,
3384
+ reject
3385
+ });
3386
+ return promise.then((claim) => assertClaim(childThreadId, claim));
3387
+ },
3388
+ onDispose: () => {
3389
+ foregroundClosed = true;
3390
+ rejectPendingAdmissions("native hook relay registration closed");
3391
+ }
3392
+ },
3393
+ onPreToolUseFailure: params.onPreToolUseFailure,
3394
+ command: {
3395
+ nice: 10,
3396
+ timeoutMs: params.options?.gatewayTimeoutMs
3397
+ }
3398
+ });
3399
+ const unregister = () => {
3400
+ foregroundClosed = true;
3401
+ rejectPendingAdmissions("native hook relay foreground closed");
3402
+ relay.unregister();
3403
+ };
3404
+ return {
3405
+ ...relay,
3406
+ unregister,
3407
+ authorizeRetentionAfterSuccessfulYield: () => {
3408
+ successfulYieldRetentionAuthorized = true;
3409
+ },
3410
+ hasClaimedDirectChild: () => directChildClaims.size > 0,
3411
+ rejectPendingDirectChild: (threadIdInput, reason) => {
3412
+ const threadId = threadIdInput.trim();
3413
+ const pending = threadId ? pendingDirectChildAdmissions.get(threadId) : void 0;
3414
+ if (!pending) return;
3415
+ pendingDirectChildAdmissions.delete(threadId);
3416
+ pending.reject(new Error(reason));
3417
+ },
3418
+ claimDirectChild: (threadIdInput) => {
3419
+ const threadId = threadIdInput.trim();
3420
+ if (!threadId) return () => void 0;
3421
+ if (directChildClaims.get(threadId)) return () => void 0;
3422
+ const claim = Symbol(threadId);
3423
+ directChildClaims.set(threadId, claim);
3424
+ const pending = pendingDirectChildAdmissions.get(threadId);
3425
+ pendingDirectChildAdmissions.delete(threadId);
3426
+ pending?.resolve(claim);
3427
+ let released = false;
3428
+ return () => {
3429
+ if (released) return;
3430
+ released = true;
3431
+ if (directChildClaims.get(threadId) !== claim) return;
3432
+ directChildClaims.delete(threadId);
3433
+ if (foregroundClosed && directChildClaims.size === 0) relay.unregister();
3434
+ };
3435
+ }
3436
+ };
3437
+ }
3438
+ function readCodexNativeChildThreadId(rawPayload) {
3439
+ if (!isJsonObject(rawPayload) || typeof rawPayload.agent_id !== "string") return;
3440
+ return rawPayload.agent_id.trim() || void 0;
3441
+ }
3442
+ /** Selects the native hook events Codex should install for the current approval mode. */
3443
+ function resolveCodexNativeHookRelayEvents(params) {
3444
+ if (params.configuredEvents?.length) return params.configuredEvents;
3445
+ return params.appServer.approvalPolicy === "never" ? CODEX_NATIVE_HOOK_RELAY_EVENTS : CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS;
3446
+ }
3447
+ /** Derives the native hook relay TTL from the turn budget unless explicitly configured. */
3448
+ function resolveCodexNativeHookRelayTtlMs(params) {
3449
+ if (params.explicitTtlMs !== void 0) return params.explicitTtlMs;
3450
+ const relayBudgetMs = params.attemptTimeoutMs + params.startupTimeoutMs + params.turnStartTimeoutMs + CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS;
3451
+ return Math.max(CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS, Math.floor(relayBudgetMs));
3452
+ }
3453
+ /** Builds a stable relay id scoped to the agent and session identity. */
3454
+ function buildCodexNativeHookRelayId(params) {
3455
+ const hash = createHash("sha256");
3456
+ hash.update("openclaw:codex:native-hook-relay:v1");
3457
+ hash.update("\0");
3458
+ hash.update(params.agentId?.trim() || "");
3459
+ hash.update("\0");
3460
+ hash.update(params.sessionKey?.trim() || params.sessionId);
3461
+ return `codex-${hash.digest("hex").slice(0, 40)}`;
3462
+ }
3463
+ const CODEX_HOOK_EVENT_BY_NATIVE_EVENT = {
3464
+ pre_tool_use: "PreToolUse",
3465
+ post_tool_use: "PostToolUse",
3466
+ permission_request: "PermissionRequest",
3467
+ before_agent_finalize: "Stop"
3468
+ };
3469
+ const CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT = {
3470
+ pre_tool_use: "pre_tool_use",
3471
+ post_tool_use: "post_tool_use",
3472
+ permission_request: "permission_request",
3473
+ before_agent_finalize: "stop"
3474
+ };
3475
+ const CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS = ["/<session-flags>/config.toml", "<session-flags>/config.toml"];
3476
+ /** Builds the Codex config overlay that installs trusted command hooks for relay events. */
3477
+ function buildCodexNativeHookRelayConfig(params) {
3478
+ const events = params.events?.length ? params.events : CODEX_NATIVE_HOOK_RELAY_EVENTS;
3479
+ const selectedEvents = new Set(events);
3480
+ const config = { "features.hooks": true };
3481
+ const hookState = {};
3482
+ for (const event of CODEX_NATIVE_HOOK_RELAY_EVENTS) {
3483
+ const codexEvent = CODEX_HOOK_EVENT_BY_NATIVE_EVENT[event];
3484
+ const selected = selectedEvents.has(event);
3485
+ const shouldRelay = params.relay.shouldRelayEvent(event);
3486
+ if (!selected || !shouldRelay) {
3487
+ if (selected || params.clearOmittedEvents) config[`hooks.${codexEvent}`] = [];
3488
+ 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 };
3489
+ continue;
3490
+ }
3491
+ const timeout = normalizeHookTimeoutSec(params.hookTimeoutSec);
3492
+ const command = params.relay.commandForEvent(event, { timeoutMs: resolveCodexNativeHookRelayCommandTimeoutMs(timeout) });
3493
+ const matcher = buildCodexNativeToolMatcher(params.relay.toolMatcherForEvent(event));
3494
+ config[`hooks.${codexEvent}`] = [{
3495
+ ...matcher ? { matcher } : {},
3496
+ hooks: [{
3497
+ type: "command",
3498
+ command,
3499
+ timeout,
3500
+ async: false,
3501
+ statusMessage: "OpenClaw native hook relay"
3502
+ }]
3503
+ }];
3504
+ const state = {
3505
+ enabled: true,
3506
+ trusted_hash: codexCommandHookTrustedHash({
3507
+ event,
3508
+ command,
3509
+ matcher,
3510
+ timeout,
3511
+ statusMessage: "OpenClaw native hook relay"
3512
+ })
3513
+ };
3514
+ for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = state;
3515
+ }
3516
+ config["hooks.state"] = hookState;
3517
+ return config;
3518
+ }
3519
+ /** Builds a Codex config overlay that disables native hooks and clears hook arrays. */
3520
+ function buildCodexNativeHookRelayDisabledConfig() {
3521
+ return {
3522
+ "features.hooks": false,
3523
+ "hooks.PreToolUse": [],
3524
+ "hooks.PostToolUse": [],
3525
+ "hooks.PermissionRequest": [],
3526
+ "hooks.Stop": []
3527
+ };
3528
+ }
3529
+ function normalizeHookTimeoutSec(value) {
3530
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.ceil(value) : CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC;
3531
+ }
3532
+ function resolveCodexNativeHookRelayCommandTimeoutMs(hookTimeoutSec) {
3533
+ const parentTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 5e3;
3534
+ 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)));
3535
+ return Math.max(1, parentTimeoutMs - parentMarginMs);
3536
+ }
3537
+ function buildCodexNativeToolMatcher(toolNames) {
3538
+ if (toolNames === void 0) return;
3539
+ if (toolNames.length === 0) throw new TypeError("Codex native hook matcher requires at least one tool name");
3540
+ const nativeNames = /* @__PURE__ */ new Set();
3541
+ let hasCustomToolName = false;
3542
+ for (const toolName of toolNames) {
3543
+ const canonicalToolName = toolName.trim();
3544
+ if (!canonicalToolName || canonicalToolName === "*") throw new TypeError("Codex native hook matcher requires canonical OpenClaw tool ids");
3545
+ const nativeAliases = CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID[canonicalToolName];
3546
+ if (!nativeAliases) hasCustomToolName = true;
3547
+ for (const nativeName of nativeAliases ?? [canonicalToolName]) nativeNames.add(nativeName);
3548
+ }
3549
+ const sortedNames = Array.from(nativeNames).toSorted();
3550
+ if (!hasCustomToolName && sortedNames.every((toolName) => /^[A-Za-z0-9_]+$/.test(toolName))) return sortedNames.join("|");
3551
+ return `(?i)^(?:${sortedNames.map((toolName) => toolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})$`;
3552
+ }
3553
+ function codexCommandHookTrustedHash(params) {
3554
+ const identity = {
3555
+ event_name: CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[params.event],
3556
+ ...params.matcher ? { matcher: params.matcher } : {},
3557
+ hooks: [{
3558
+ async: false,
3559
+ command: params.command,
3560
+ statusMessage: params.statusMessage,
3561
+ timeout: params.timeout,
3562
+ type: "command"
3563
+ }]
3564
+ };
3565
+ return `sha256:${createHash("sha256").update(JSON.stringify(sortJsonValue(identity))).digest("hex")}`;
3566
+ }
3567
+ function sortJsonValue(value) {
3568
+ if (!value || typeof value !== "object") return value;
3569
+ if (Array.isArray(value)) return value.map(sortJsonValue);
3570
+ const sorted = {};
3571
+ for (const [key, entry] of Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right))) sorted[key] = sortJsonValue(entry);
3572
+ return sorted;
3573
+ }
3574
+ //#endregion
3575
+ //#region extensions/codex/src/app-server/profiler-flag.ts
3576
+ const PROFILER_FLAGS = ["profiler", "codex.profiler"];
3577
+ /** Checks the generic and Codex-specific profiler diagnostic flags. */
3578
+ function isCodexAppServerProfilerEnabled(config, env = process.env) {
3579
+ return PROFILER_FLAGS.some((flag) => isDiagnosticFlagEnabled(flag, config, env));
3580
+ }
3581
+ //#endregion
3582
+ //#region extensions/codex/src/app-server/thread-lifecycle-timing.ts
3583
+ const CODEX_THREAD_LIFECYCLE_TIMING_WARN_TOTAL_MS = 1e3;
3584
+ const CODEX_THREAD_LIFECYCLE_TIMING_WARN_STAGE_MS = 500;
3585
+ function shouldWarnCodexThreadLifecycleTimingSummary(summary, options = {}) {
3586
+ const totalThresholdMs = options.totalThresholdMs ?? CODEX_THREAD_LIFECYCLE_TIMING_WARN_TOTAL_MS;
3587
+ const stageThresholdMs = options.stageThresholdMs ?? CODEX_THREAD_LIFECYCLE_TIMING_WARN_STAGE_MS;
3588
+ return summary.totalMs >= totalThresholdMs || summary.spans.some((span) => span.durationMs >= stageThresholdMs);
3589
+ }
3590
+ function formatCodexThreadLifecycleTimingSummary(params) {
3591
+ const spans = params.summary.spans.length > 0 ? params.summary.spans.map((span) => `${span.name}:${span.durationMs}ms@${span.elapsedMs}ms`).join(",") : "none";
3592
+ return `[trace:codex-app-server] thread lifecycle: runId=${params.runId} sessionId=${params.sessionId} sessionKey=${params.sessionKey ?? "unknown"} action=${params.action} totalMs=${params.summary.totalMs} stages=${spans}`;
3593
+ }
3594
+ function createCodexThreadLifecycleTimingTracker(options = {}) {
3595
+ const log = options.log ?? embeddedAgentLog;
3596
+ if (!options.enabled && log.isEnabled?.("trace") !== true) return {
3597
+ async measure(_name, run) {
3598
+ return await run();
3599
+ },
3600
+ measureSync(_name, run) {
3601
+ return run();
3602
+ },
3603
+ mark() {},
3604
+ logSummary() {}
3605
+ };
3606
+ const now = options.now ?? Date.now;
3607
+ const startedAt = now();
3608
+ let didLog = false;
3609
+ const spans = [];
3610
+ const toMs = (value) => Math.max(0, Math.round(value));
3611
+ const record = (name, spanStartedAt) => {
3612
+ const currentAt = now();
3613
+ spans.push({
3614
+ name,
3615
+ durationMs: toMs(currentAt - spanStartedAt),
3616
+ elapsedMs: toMs(currentAt - startedAt)
3617
+ });
3618
+ };
3619
+ const snapshot = () => ({
3620
+ totalMs: toMs(now() - startedAt),
3621
+ spans: spans.slice()
3622
+ });
3623
+ return {
3624
+ async measure(name, run) {
3625
+ const spanStartedAt = now();
3626
+ try {
3627
+ return await run();
3628
+ } finally {
3629
+ record(name, spanStartedAt);
3630
+ }
3631
+ },
3632
+ measureSync(name, run) {
3633
+ const spanStartedAt = now();
3634
+ try {
3635
+ return run();
3636
+ } finally {
3637
+ record(name, spanStartedAt);
3638
+ }
3639
+ },
3640
+ mark(name) {
3641
+ record(name, now());
3642
+ },
3643
+ logSummary(params) {
3644
+ if (didLog) return;
3645
+ const summary = snapshot();
3646
+ const shouldWarn = shouldWarnCodexThreadLifecycleTimingSummary(summary, options);
3647
+ if (!shouldWarn && !log.isEnabled?.("trace")) return;
3648
+ didLog = true;
3649
+ const message = formatCodexThreadLifecycleTimingSummary({
3650
+ runId: params.runId,
3651
+ sessionId: params.sessionId,
3652
+ sessionKey: params.sessionKey,
3653
+ action: params.action,
3654
+ summary
3655
+ });
3656
+ const meta = {
3657
+ runId: params.runId,
3658
+ sessionId: params.sessionId,
3659
+ sessionKey: params.sessionKey,
3660
+ action: params.action,
3661
+ threadId: params.threadId,
3662
+ totalMs: summary.totalMs,
3663
+ spans: summary.spans
3664
+ };
3665
+ if (shouldWarn) log.warn(message, meta);
3666
+ else log.trace(message, meta);
3667
+ }
3668
+ };
3669
+ }
3670
+ //#endregion
3671
+ //#region extensions/codex/src/app-server/thread-lifecycle-preflight.ts
3672
+ async function prepareCodexThreadLifecyclePreflight(params) {
3673
+ await assertCodexModelBackedReviewerEffectiveConfig({
3674
+ client: params.client,
3675
+ approvalsReviewer: params.appServer.approvalsReviewer,
3676
+ cwd: params.cwd,
3677
+ signal: params.signal
3678
+ });
3679
+ if (params.nativeHookRelayRequired) await assertCodexNativeHookRelayAllowed(params.client, params.signal);
3680
+ const lifecycleTiming = createCodexThreadLifecycleTimingTracker({
3681
+ ...params.timing,
3682
+ enabled: params.timing?.enabled ?? isCodexAppServerProfilerEnabled(params.params.config)
3683
+ });
3684
+ const legacyDynamicToolsFingerprint = lifecycleTiming.measureSync("legacy-dynamic-tools-fingerprint", () => codexLegacyDynamicToolsFingerprint(params.dynamicTools));
3685
+ const dynamicToolsFingerprint = lifecycleTiming.measureSync("dynamic-tools-fingerprint", () => hashCodexAppServerBindingFingerprint(legacyDynamicToolsFingerprint));
3686
+ const dynamicToolsContainDeferred = flattenCodexDynamicToolFunctions(params.dynamicTools).some((tool) => tool.deferLoading === true);
3687
+ const webSearchThreadConfigFingerprint = fingerprintJsonObject(lifecycleTiming.measureSync("web-search-plan", () => resolveCodexWebSearchPlan({
3688
+ config: params.params.config,
3689
+ disableTools: params.params.disableTools,
3690
+ nativeToolSurfaceEnabled: params.nativeCodeModeEnabled,
3691
+ nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
3692
+ webSearchAllowed: params.webSearchAllowed
3693
+ })).threadConfig);
3694
+ const networkProxyConfigFingerprint = params.appServer.networkProxy?.configFingerprint;
3695
+ const contextEngineBinding = lifecycleTiming.measureSync("context-engine-binding", () => buildContextEngineBinding(params.params, params.contextEngineProjection));
3696
+ const userMcpServersConfigPatch = params.userMcpServersEnabled === false ? void 0 : await buildCodexUserMcpServersThreadConfigPatchForRun({
3697
+ run: params.params,
3698
+ cwd: params.cwd,
3699
+ agentId: params.agentId ?? params.params.agentId,
3700
+ allowLiteralOAuthProjection: params.appServer.connectionClass !== "remote",
3701
+ warn: (message) => embeddedAgentLog.warn(message),
3702
+ onServerUnavailable: (serverName, error) => embeddedAgentLog.warn("skipping unavailable MCP OAuth server", {
3703
+ serverName,
3704
+ error: formatErrorMessage(error)
3705
+ })
3706
+ });
3707
+ const nativeSkillIsolation = await lifecycleTiming.measure("native-skill-isolation", () => resolveCodexNativeSkillIsolation({
3708
+ client: params.client,
3709
+ codexHome: params.appServer.start.env?.CODEX_HOME,
3710
+ cwd: params.cwd,
3711
+ home: params.appServer.start.env?.HOME,
3712
+ signal: params.signal,
3713
+ userProfile: params.appServer.start.env?.USERPROFILE
3714
+ }));
3715
+ const nativeSkillIsolationFingerprint = nativeSkillIsolation ? fingerprintJsonObject({
3716
+ version: 1,
3717
+ disabledUserSkillPaths: nativeSkillIsolation.disabledUserSkillPaths
3718
+ }) : void 0;
3719
+ const legacyUserMcpServersFingerprint = legacyFingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
3720
+ const userMcpServersFingerprint = fingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
3721
+ const environmentSelectionFingerprint = fingerprintEnvironmentSelection(params.environmentSelection);
3722
+ const hostSystemAgentActive = params.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw");
3723
+ const ringZeroActive = hostSystemAgentActive && isSystemAgentOnlyCodexDynamicToolAllowlist(params.params.toolsAllow);
3724
+ const messageOnlySourceReply = isMessageOnlyCodexSourceReply(params.params);
3725
+ const restrictedToolSurface = ringZeroActive || messageOnlySourceReply || params.params.pluginHarnessToolPolicyRestricted === true;
3726
+ const imageGenerationDenied = params.params.pluginHarnessToolPolicySafeDeniedTools?.includes("image_generate") === true;
3727
+ if (restrictedToolSurface && params.nativeCodeModeEnabled !== false) throw new Error("Codex restricted tool surfaces require native code mode to be disabled");
3728
+ const restrictedToolSurfaceInheritedMcpServerNames = restrictedToolSurface ? await lifecycleTiming.measure("restricted-tool-surface-mcp-config-read", () => readCodexInheritedMcpServerNames(params.client, params.cwd, params.signal)) : [];
3729
+ if (restrictedToolSurface || imageGenerationDenied) await lifecycleTiming.measure("tool-policy-config-requirements-read", () => assertCodexManagedRequirementsDoNotOverrideToolPolicy(params.client, {
3730
+ restrictedToolSurface,
3731
+ additionalDeniedFeatures: imageGenerationDenied ? ["image_generation"] : void 0
3732
+ }, params.signal));
3733
+ const ringZeroConfigFingerprint = ringZeroActive ? fingerprintJsonObject({
3734
+ version: 1,
3735
+ baseInstructions: "",
3736
+ config: buildCodexRingZeroThreadConfigPatch(params.params, true, restrictedToolSurfaceInheritedMcpServerNames)
3737
+ }) : void 0;
3738
+ return {
3739
+ contextEngineBinding,
3740
+ dynamicToolsContainDeferred,
3741
+ dynamicToolsFingerprint,
3742
+ environmentSelectionFingerprint,
3743
+ hostSystemAgentActive,
3744
+ legacyDynamicToolsFingerprint,
3745
+ legacyUserMcpServersFingerprint,
3746
+ lifecycleTiming,
3747
+ nativeSkillIsolation,
3748
+ nativeSkillIsolationFingerprint,
3749
+ networkProxyConfigFingerprint,
3750
+ ringZeroActive,
3751
+ ringZeroClientInstanceId: ringZeroActive ? getCodexAppServerClientInstanceId(params.client) : void 0,
3752
+ ringZeroConfigFingerprint,
3753
+ restrictedToolSurface,
3754
+ restrictedToolSurfaceInheritedMcpServerNames,
3755
+ userMcpServersConfigPatch,
3756
+ userMcpServersFingerprint,
3757
+ webSearchThreadConfigFingerprint
3758
+ };
3759
+ }
3760
+ //#endregion
3041
3761
  //#region extensions/codex/src/app-server/thread-supervision.ts
3042
3762
  async function materializePendingSupervisionBranch(params) {
3043
3763
  let pending = params.binding.pendingSupervisionBranch;
@@ -3061,6 +3781,32 @@ async function materializePendingSupervisionBranch(params) {
3061
3781
  });
3062
3782
  let bindingCommitted = false;
3063
3783
  let provisionalCleanupSafe = true;
3784
+ let cleanupExpected = pending;
3785
+ const trackPendingSupervisionArtifacts = async (cleanupThreadIds) => {
3786
+ const expected = pending;
3787
+ pending = withPendingSupervisionCleanup(pending, cleanupThreadIds);
3788
+ let updated;
3789
+ try {
3790
+ updated = await params.bindingStore.mutate(params.bindingIdentity, {
3791
+ kind: "patch-pending-supervision-branch",
3792
+ expected,
3793
+ pending
3794
+ });
3795
+ } catch (error) {
3796
+ try {
3797
+ const current = await params.bindingStore.read(params.bindingIdentity);
3798
+ if (matchesPendingSupervisionState(current, pending)) cleanupExpected = pending;
3799
+ else if (matchesPendingSupervisionState(current, expected)) cleanupExpected = expected;
3800
+ else throw new CodexThreadBindingConflictError(pending.sourceThreadId, "verifying supervised Codex cleanup tracking");
3801
+ } catch (verificationError) {
3802
+ provisionalCleanupSafe = false;
3803
+ throw new CodexAppServerUnsafeSubscriptionError(`Codex supervised branch cleanup tracking could not be verified: ${cleanupThreadIds.join(", ")}`, { cause: new AggregateError([error, verificationError], void 0, { cause: error }) });
3804
+ }
3805
+ throw error;
3806
+ }
3807
+ cleanupExpected = updated ? pending : void 0;
3808
+ if (!updated) throw new CodexThreadBindingConflictError(pending.sourceThreadId, "tracking supervised Codex branch cleanup");
3809
+ };
3064
3810
  try {
3065
3811
  const probeParams = buildPendingSupervisionProbeForkParams(params, pending);
3066
3812
  const rawProbeResponse = await params.lifecycleTiming.measure("supervision-model-probe-fork", async () => {
@@ -3076,10 +3822,17 @@ async function materializePendingSupervisionBranch(params) {
3076
3822
  sourceThreadId: pending.sourceThreadId,
3077
3823
  role: "model probe"
3078
3824
  });
3079
- pending = await trackPendingSupervisionArtifacts(params, pending, [probeThreadId]);
3825
+ let probeResponse;
3826
+ try {
3827
+ params.throwIfAborted();
3828
+ probeResponse = assertCodexThreadForkResponse(rawProbeResponse);
3829
+ if (params.restrictedToolSurface) await params.lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, probeThreadId, probeParams.config ?? void 0, params.signal));
3830
+ } finally {
3831
+ await unsubscribeCodexAppServerLiveThread(params.client, probeThreadId, CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS).catch((cause) => {
3832
+ throw new CodexAppServerUnsafeSubscriptionError(`Codex model probe subscription could not be released: ${probeThreadId}`, { cause });
3833
+ });
3834
+ }
3080
3835
  params.throwIfAborted();
3081
- const probeResponse = assertCodexThreadForkResponse(rawProbeResponse);
3082
- if (params.restrictedToolSurface) await params.lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, probeThreadId, probeParams.config ?? void 0, params.signal));
3083
3836
  const nativeModel = requireNonBlankSupervisionValue(probeResponse.model, "native model");
3084
3837
  const nativeModelProvider = requireNativeSupervisionModelProvider({
3085
3838
  responseModelProvider: probeResponse.modelProvider,
@@ -3102,7 +3855,9 @@ async function materializePendingSupervisionBranch(params) {
3102
3855
  model: nativeModel,
3103
3856
  modelProvider: nativeModelProvider,
3104
3857
  hostSystemAgentActive: params.hostSystemAgentActive,
3105
- restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames
3858
+ restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames,
3859
+ shellEnvironment: params.shellEnvironment,
3860
+ disableLoginShell: params.disableLoginShell
3106
3861
  });
3107
3862
  assertExactSupervisionModelSelection(startParams, {
3108
3863
  model: nativeModel,
@@ -3123,7 +3878,7 @@ async function materializePendingSupervisionBranch(params) {
3123
3878
  otherThreadId: probeThreadId,
3124
3879
  role: "canonical branch"
3125
3880
  });
3126
- pending = await trackPendingSupervisionArtifacts(params, pending, [probeThreadId, finalThreadId]);
3881
+ await trackPendingSupervisionArtifacts([finalThreadId]);
3127
3882
  params.throwIfAborted();
3128
3883
  assertExactSupervisionModelSelection(assertCodexThreadStartResponse(rawStartResponse), {
3129
3884
  model: nativeModel,
@@ -3143,11 +3898,11 @@ async function materializePendingSupervisionBranch(params) {
3143
3898
  client: params.client,
3144
3899
  threadId: finalThreadId,
3145
3900
  ephemeral: startParams.ephemeral === true
3146
- }) || !await archiveSupervisionArtifact(params.client, probeThreadId)) {
3901
+ })) {
3147
3902
  provisionalCleanupSafe = false;
3148
3903
  throw new CodexAppServerUnsafeSubscriptionError("Codex supervised plugin app attestation cleanup failed", { cause: error });
3149
3904
  }
3150
- pending = await trackPendingSupervisionArtifacts(params, pending, []);
3905
+ await trackPendingSupervisionArtifacts([]);
3151
3906
  throw error;
3152
3907
  }
3153
3908
  if (history.responseItems.length > 0) {
@@ -3157,8 +3912,6 @@ async function materializePendingSupervisionBranch(params) {
3157
3912
  }, { signal: params.signal }));
3158
3913
  params.throwIfAborted();
3159
3914
  }
3160
- if (!await archiveSupervisionArtifact(params.client, probeThreadId)) throw new Error(`Failed to archive temporary Codex model probe: ${probeThreadId}`);
3161
- pending = await trackPendingSupervisionArtifacts(params, pending, [finalThreadId]);
3162
3915
  const historyCoveredThrough = (/* @__PURE__ */ new Date()).toISOString();
3163
3916
  const bindingModelProvider = params.normalizeBindingModelProvider(params.attempt.authProfileId, nativeModelProvider);
3164
3917
  let committed = false;
@@ -3220,31 +3973,28 @@ async function materializePendingSupervisionBranch(params) {
3220
3973
  };
3221
3974
  } catch (error) {
3222
3975
  if (bindingCommitted) throw error;
3223
- if (error instanceof CodexThreadBindingConflictAfterCleanupError) throw error;
3224
3976
  if (!provisionalCleanupSafe) {
3225
3977
  await params.abandonClient();
3226
3978
  throw error;
3227
3979
  }
3228
3980
  const cleanup = await cleanPendingSupervisionArtifacts(params.client, pending);
3981
+ const nextPending = withPendingSupervisionCleanup(pending, cleanup.remaining);
3229
3982
  let cleanupStateError;
3230
- if (cleanup.remaining.length !== (pending.cleanupThreadIds?.length ?? 0)) {
3231
- const nextPending = withPendingSupervisionCleanup(pending, cleanup.remaining);
3232
- try {
3233
- if (await params.bindingStore.mutate(params.bindingIdentity, {
3234
- kind: "patch-pending-supervision-branch",
3235
- expected: pending,
3236
- pending: nextPending
3237
- })) pending = nextPending;
3238
- } catch (stateError) {
3239
- cleanupStateError = stateError;
3240
- }
3983
+ if (cleanupExpected && !isDeepStrictEqual(cleanupExpected, nextPending)) try {
3984
+ await params.bindingStore.mutate(params.bindingIdentity, {
3985
+ kind: "patch-pending-supervision-branch",
3986
+ expected: cleanupExpected,
3987
+ pending: nextPending
3988
+ });
3989
+ } catch (stateError) {
3990
+ cleanupStateError = stateError;
3241
3991
  }
3242
3992
  const unsafeCleanup = cleanup.remaining.length > 0 || isCodexAppServerUnsafeSubscriptionError(error);
3243
3993
  if (unsafeCleanup) await params.abandonClient();
3244
3994
  if (cleanupStateError) {
3245
- const cause = new AggregateError([error, cleanupStateError]);
3995
+ const cause = new AggregateError([error, cleanupStateError], "Codex supervised branch cleanup state could not be recorded", { cause: error });
3246
3996
  if (unsafeCleanup) throw new CodexAppServerUnsafeSubscriptionError("Codex supervised branch cleanup state could not be recorded", { cause });
3247
- throw new AggregateError([error, cleanupStateError], "Codex supervised branch cleanup state could not be recorded", { cause: error });
3997
+ throw cause;
3248
3998
  }
3249
3999
  if (cleanup.remaining.length > 0) throw new CodexAppServerUnsafeSubscriptionError(`Codex supervised branch cleanup remains pending: ${cleanup.remaining.join(", ")}`, { cause: error });
3250
4000
  throw error;
@@ -3258,7 +4008,9 @@ function buildPendingSupervisionProbeForkParams(params, pending) {
3258
4008
  webSearchAllowed: params.webSearchAllowed,
3259
4009
  appServer: params.appServer,
3260
4010
  hostSystemAgentActive: params.hostSystemAgentActive,
3261
- restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames
4011
+ restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames,
4012
+ shellEnvironment: params.shellEnvironment,
4013
+ disableLoginShell: params.disableLoginShell
3262
4014
  });
3263
4015
  return {
3264
4016
  threadId: pending.sourceThreadId,
@@ -3270,7 +4022,7 @@ function buildPendingSupervisionProbeForkParams(params, pending) {
3270
4022
  ...params.appServer.serviceTier !== void 0 ? { serviceTier: params.appServer.serviceTier } : {},
3271
4023
  config: runtimeConfig,
3272
4024
  developerInstructions: params.developerInstructions ?? buildDeveloperInstructions(params.attempt, { dynamicTools: params.dynamicTools }),
3273
- ephemeral: isIncognitoSessionKey(params.attempt.sessionKey),
4025
+ ephemeral: true,
3274
4026
  threadSource: "appServer",
3275
4027
  excludeTurns: true
3276
4028
  };
@@ -3312,10 +4064,8 @@ function requireDistinctSupervisionThreadId(params) {
3312
4064
  return threadId;
3313
4065
  }
3314
4066
  function readSupervisionResponseThreadId(value) {
3315
- if (!value || typeof value !== "object" || Array.isArray(value)) return;
3316
- const thread = value.thread;
3317
- if (!thread || typeof thread !== "object" || Array.isArray(thread)) return;
3318
- return thread.id;
4067
+ const thread = isRecord(value) ? value.thread : void 0;
4068
+ return isRecord(thread) ? thread.id : void 0;
3319
4069
  }
3320
4070
  async function recoverPendingSupervisionArtifacts(params, pending) {
3321
4071
  if (!pending.cleanupThreadIds?.length) return pending;
@@ -3338,20 +4088,6 @@ async function recoverPendingSupervisionArtifacts(params, pending) {
3338
4088
  })) throw new CodexThreadBindingConflictError(pending.sourceThreadId, "recovering a supervised Codex branch");
3339
4089
  return next;
3340
4090
  }
3341
- async function trackPendingSupervisionArtifacts(params, pending, cleanupThreadIds) {
3342
- const next = withPendingSupervisionCleanup(pending, cleanupThreadIds);
3343
- if (!await params.bindingStore.mutate(params.bindingIdentity, {
3344
- kind: "patch-pending-supervision-branch",
3345
- expected: pending,
3346
- pending: next
3347
- })) {
3348
- const cleanupFailed = [];
3349
- for (const threadId of cleanupThreadIds) if (!await archiveSupervisionArtifact(params.client, threadId)) cleanupFailed.push(threadId);
3350
- if (cleanupFailed.length > 0) throw new CodexAppServerUnsafeSubscriptionError(`Codex supervised branch CAS cleanup failed: ${cleanupFailed.join(", ")}`);
3351
- throw new CodexThreadBindingConflictAfterCleanupError(pending.sourceThreadId, "tracking supervised Codex branch cleanup");
3352
- }
3353
- return next;
3354
- }
3355
4091
  function withPendingSupervisionCleanup(pending, cleanupThreadIds) {
3356
4092
  return {
3357
4093
  sourceThreadId: pending.sourceThreadId,
@@ -3388,15 +4124,10 @@ async function archiveSupervisionArtifact(client, threadId) {
3388
4124
  async function startOrResumeThread(params) {
3389
4125
  const incognito = isIncognitoSessionKey(params.params.sessionKey);
3390
4126
  const clientId = resolveCodexAppServerClientInstanceId(params.client);
3391
- const bindingIdentity = sessionBindingIdentity({
3392
- sessionId: params.params.sessionId,
3393
- sessionKey: params.params.sessionKey,
3394
- agentId: params.agentId ?? params.params.agentId,
3395
- config: params.params.config
3396
- });
3397
- return await params.bindingStore.withLease(bindingIdentity, async () => {
3398
- const { contextEngineBinding, dynamicToolsContainDeferred, dynamicToolsFingerprint, environmentSelectionFingerprint, hostSystemAgentActive, legacyDynamicToolsFingerprint, legacyUserMcpServersFingerprint, lifecycleTiming, nativeSkillIsolation, nativeSkillIsolationFingerprint, networkProxyConfigFingerprint, ringZeroActive, ringZeroClientInstanceId, ringZeroConfigFingerprint, restrictedToolSurface, restrictedToolSurfaceInheritedMcpServerNames, userMcpServersConfigPatch, userMcpServersFingerprint, webSearchThreadConfigFingerprint } = await prepareCodexThreadLifecyclePreflight(params);
3399
- let binding = await lifecycleTiming.measure("read-binding", () => params.bindingStore.read(bindingIdentity));
4127
+ return await withCodexThreadLifecycleBinding(params, async (bindingIdentity, currentBinding) => {
4128
+ let binding = currentBinding;
4129
+ const preflight = await prepareCodexThreadLifecyclePreflight(params);
4130
+ const { contextEngineBinding, dynamicToolsContainDeferred, dynamicToolsFingerprint, environmentSelectionFingerprint, hostSystemAgentActive, legacyDynamicToolsFingerprint, legacyUserMcpServersFingerprint, lifecycleTiming, nativeSkillIsolation, nativeSkillIsolationFingerprint, networkProxyConfigFingerprint, ringZeroActive, ringZeroClientInstanceId, ringZeroConfigFingerprint, restrictedToolSurface, restrictedToolSurfaceInheritedMcpServerNames, userMcpServersConfigPatch, userMcpServersFingerprint, webSearchThreadConfigFingerprint } = preflight;
3400
4131
  let replacementPredecessor;
3401
4132
  const initialBoundThreadId = binding?.threadId;
3402
4133
  const initialBoundClientId = binding?.clientId;
@@ -3408,26 +4139,33 @@ async function startOrResumeThread(params) {
3408
4139
  config: params.params.config
3409
4140
  });
3410
4141
  const throwIfAborted = () => throwIfCodexThreadLifecycleAborted(params.signal);
3411
- const releaseRetainedThread = async (threadId, ownerClientId = initialBoundClientId) => {
4142
+ const releaseRetainedThread = async (threadId, ownerClientId = initialBoundClientId, assertCurrent) => {
3412
4143
  if (ownerClientId && ownerClientId !== clientId) {
3413
4144
  const previousClient = retainSharedCodexAppServerClientByInstanceId(ownerClientId);
3414
- if (!previousClient) return;
4145
+ if (!previousClient) return false;
3415
4146
  try {
3416
- await releaseCodexRetainedLiveThread({
4147
+ const assertPrevious = assertCurrent ? captureExclusiveSharedCodexAppServerClient(previousClient.client) : void 0;
4148
+ if (isCodexAppServerLiveThreadClaimed(previousClient.client, threadId)) throw new Error(`Codex thread ${threadId} is claimed by active work; stop it first.`);
4149
+ return await releaseCodexRetainedLiveThread({
3417
4150
  client: previousClient.client,
3418
4151
  lifecycleTiming,
3419
- threadId
4152
+ threadId,
4153
+ assertCurrent: assertCurrent ? () => {
4154
+ assertCurrent();
4155
+ assertPrevious?.();
4156
+ } : void 0
3420
4157
  });
3421
4158
  } finally {
3422
4159
  previousClient.release();
3423
4160
  }
3424
- return;
3425
4161
  }
3426
- await releaseCodexRetainedLiveThread({
4162
+ if (isCodexAppServerLiveThreadClaimed(params.client, threadId)) throw new Error(`Codex thread ${threadId} is claimed by active work; stop it first.`);
4163
+ return await releaseCodexRetainedLiveThread({
3427
4164
  client: params.client,
3428
4165
  abandonClient: params.abandonClient,
3429
4166
  lifecycleTiming,
3430
- threadId
4167
+ threadId,
4168
+ assertCurrent
3431
4169
  });
3432
4170
  };
3433
4171
  if (!binding && bindingIdentity.kind === "session" && bindingIdentity.sessionKey) {
@@ -3465,6 +4203,8 @@ async function startOrResumeThread(params) {
3465
4203
  hostSystemAgentActive,
3466
4204
  restrictedToolSurface,
3467
4205
  restrictedToolSurfaceInheritedMcpServerNames,
4206
+ shellEnvironment: params.shellEnvironment,
4207
+ disableLoginShell: params.disableLoginShell,
3468
4208
  environmentSelection: params.environmentSelection,
3469
4209
  provisionalAppIds: pluginThreadConfig?.provisionalAppIds,
3470
4210
  signal: params.signal,
@@ -3475,6 +4215,7 @@ async function startOrResumeThread(params) {
3475
4215
  cwd: params.cwd,
3476
4216
  ...clientId ? { clientId } : {},
3477
4217
  authProfileId: void 0,
4218
+ agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
3478
4219
  preserveNativeModel: true,
3479
4220
  dynamicToolsFingerprint,
3480
4221
  dynamicToolsContainDeferred,
@@ -3506,6 +4247,36 @@ async function startOrResumeThread(params) {
3506
4247
  })) throw new CodexThreadBindingConflictError(current.threadId, operation);
3507
4248
  binding = void 0;
3508
4249
  };
4250
+ const resolveRequestContext = () => {
4251
+ const startModelSelection = resolveCodexAppServerThreadModelSelection({
4252
+ provider: params.params.provider,
4253
+ model: params.runtimeModelId ?? params.params.modelId,
4254
+ binding,
4255
+ authProfileId: params.params.authProfileId,
4256
+ authProfileStore: params.params.authProfileStore,
4257
+ agentDir: params.params.agentDir,
4258
+ config: params.params.config
4259
+ });
4260
+ return {
4261
+ ...preflight,
4262
+ bindingIdentity,
4263
+ startModelSelection,
4264
+ startModelProvider: startModelSelection.modelProvider,
4265
+ normalizeBindingModelProvider,
4266
+ throwIfAborted
4267
+ };
4268
+ };
4269
+ const transientDelegationRestriction = params.params.delegationCapability === "report_only";
4270
+ const persistentWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed === false;
4271
+ const transientNativeToolRestriction = params.nativeCodeModeEnabled === false && !persistentWebSearchRestriction;
4272
+ const transientWebSearchRestriction = isTransientWebSearchRestriction(params);
4273
+ if (binding?.pendingResumeConfiguration) return await resumePendingCodexThread(params, {
4274
+ ...resolveRequestContext(),
4275
+ binding,
4276
+ clearCurrentBinding,
4277
+ releaseRetainedThread: (threadId, assertCurrent) => releaseRetainedThread(threadId, initialBoundClientId, assertCurrent),
4278
+ transientRestriction: transientDelegationRestriction || transientNativeToolRestriction || transientWebSearchRestriction
4279
+ });
3509
4280
  if (binding?.threadId && !restrictedToolSurface && binding.nativeToolPolicyRestricted === true) await clearCurrentBinding("rotating a host-policy-restricted thread binding");
3510
4281
  if (binding?.threadId && binding.nativeSkillIsolationFingerprint !== nativeSkillIsolationFingerprint) {
3511
4282
  embeddedAgentLog.debug("codex app-server native skill isolation changed; starting a new thread", { threadId: binding.threadId });
@@ -3539,24 +4310,12 @@ async function startOrResumeThread(params) {
3539
4310
  await clearCurrentBinding("rotating a GPT-5.6 multi-agent thread binding");
3540
4311
  binding = void 0;
3541
4312
  }
3542
- const startModelSelection = resolveCodexAppServerThreadModelSelection({
3543
- provider: params.params.provider,
3544
- model: params.params.modelId,
3545
- binding,
3546
- authProfileId: params.params.authProfileId,
3547
- authProfileStore: params.params.authProfileStore,
3548
- agentDir: params.params.agentDir,
3549
- config: params.params.config
3550
- });
3551
- const startModelProvider = startModelSelection.modelProvider;
3552
- const transientDelegationRestriction = params.params.delegationCapability === "report_only";
4313
+ const requestContext = resolveRequestContext();
4314
+ const { startModelSelection, startModelProvider } = requestContext;
3553
4315
  let preserveExistingBinding = transientDelegationRestriction || !ringZeroActive && params.nativeProviderWebSearchSupport === "unknown" && !binding?.threadId;
3554
4316
  let rotatedContextEngineBinding = false;
3555
4317
  let prebuiltPluginThreadConfig;
3556
4318
  const webSearchBindingChanged = binding?.threadId && binding.webSearchThreadConfigFingerprint !== webSearchThreadConfigFingerprint;
3557
- const persistentWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed === false;
3558
- const transientNativeToolRestriction = params.nativeCodeModeEnabled === false && !persistentWebSearchRestriction;
3559
- const transientWebSearchRestriction = isTransientWebSearchRestriction(params);
3560
4319
  const explicitTransientWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed !== false && transientWebSearchRestriction;
3561
4320
  const unknownProviderWebSearchSupport = params.nativeProviderWebSearchSupport === "unknown";
3562
4321
  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) {
@@ -3727,31 +4486,11 @@ async function startOrResumeThread(params) {
3727
4486
  userMcpServersConfigPatch
3728
4487
  });
3729
4488
  if (warmReuse.binding) return warmReuse.binding;
4489
+ if (binding.preserveNativeModel === true) await assertAdoptedCodexThreadResumeAllowed(params, binding.threadId, requestContext);
3730
4490
  await releaseRetainedThread(binding.threadId);
3731
4491
  const resumed = await resumeExistingCodexThread(params, {
4492
+ ...requestContext,
3732
4493
  binding,
3733
- bindingIdentity,
3734
- startModelSelection,
3735
- startModelProvider,
3736
- userMcpServersConfigPatch,
3737
- dynamicToolsFingerprint,
3738
- dynamicToolsContainDeferred,
3739
- webSearchThreadConfigFingerprint,
3740
- nativeSkillIsolationFingerprint,
3741
- userMcpServersFingerprint,
3742
- ringZeroConfigFingerprint,
3743
- ringZeroClientInstanceId,
3744
- networkProxyConfigFingerprint,
3745
- contextEngineBinding,
3746
- environmentSelectionFingerprint,
3747
- hostSystemAgentActive,
3748
- ringZeroActive,
3749
- restrictedToolSurface,
3750
- restrictedToolSurfaceInheritedMcpServerNames,
3751
- nativeSkillIsolation,
3752
- lifecycleTiming,
3753
- normalizeBindingModelProvider,
3754
- throwIfAborted,
3755
4494
  clearCurrentBinding,
3756
4495
  prebuiltFinalConfigPatch: warmReuse.prebuiltFinalConfigPatch,
3757
4496
  prebuiltPluginThreadConfig
@@ -3760,28 +4499,7 @@ async function startOrResumeThread(params) {
3760
4499
  }
3761
4500
  if (initialBoundThreadId && !preserveExistingBinding && !replacementPredecessor) await releaseRetainedThread(initialBoundThreadId);
3762
4501
  const started = await startFreshCodexThread(params, {
3763
- bindingIdentity,
3764
- startModelSelection,
3765
- startModelProvider,
3766
- userMcpServersConfigPatch,
3767
- dynamicToolsFingerprint,
3768
- dynamicToolsContainDeferred,
3769
- webSearchThreadConfigFingerprint,
3770
- nativeSkillIsolationFingerprint,
3771
- userMcpServersFingerprint,
3772
- ringZeroConfigFingerprint,
3773
- ringZeroClientInstanceId,
3774
- networkProxyConfigFingerprint,
3775
- contextEngineBinding,
3776
- environmentSelectionFingerprint,
3777
- hostSystemAgentActive,
3778
- ringZeroActive,
3779
- restrictedToolSurface,
3780
- restrictedToolSurfaceInheritedMcpServerNames,
3781
- nativeSkillIsolation,
3782
- lifecycleTiming,
3783
- normalizeBindingModelProvider,
3784
- throwIfAborted,
4502
+ ...requestContext,
3785
4503
  prebuiltPluginThreadConfig,
3786
4504
  preserveExistingBinding,
3787
4505
  rotatedContextEngineBinding,
@@ -3855,9 +4573,10 @@ function buildTurnStartParams(params, options) {
3855
4573
  input: [...buildCodexUserInput(options.promptText ?? params.prompt, params.images), ...options.explicitSkillInputs ?? []],
3856
4574
  ...additionalContext ? { additionalContext } : {},
3857
4575
  cwd: options.cwd,
4576
+ ...options.appServer.sessionRoot ? { runtimeWorkspaceRoots: [options.appServer.sessionRoot] } : {},
3858
4577
  approvalPolicy: options.appServer.approvalPolicy,
3859
4578
  approvalsReviewer: options.appServer.approvalsReviewer,
3860
- ...useThreadPermissionProfile ? {} : { sandboxPolicy: options.sandboxPolicy ?? codexSandboxPolicyForTurn(options.appServer.sandbox, options.cwd, options.appServer.start?.args) },
4579
+ ...useThreadPermissionProfile ? {} : { sandboxPolicy: options.sandboxPolicy ?? codexSandboxPolicyForTurn(options.appServer.sandbox, options.appServer.sessionRoot ?? options.cwd, options.appServer.start?.args) },
3861
4580
  ...modelSelection ? {
3862
4581
  model: modelSelection.model,
3863
4582
  personality: CODEX_NATIVE_PERSONALITY_NONE
@@ -3887,7 +4606,6 @@ function buildTurnCollaborationMode(params, options = {}) {
3887
4606
  function buildTurnScopedCollaborationInstructions(params, options = {}) {
3888
4607
  const contextInstructions = joinPresentSections(options.turnScopedDeveloperInstructions, options.memoryCollaborationInstructions, options.skillsCollaborationInstructions);
3889
4608
  if (params.trigger === "cron") return joinPresentSections(buildCronCollaborationInstructions(), contextInstructions);
3890
- if (params.trigger === "heartbeat") return joinPresentSections(buildHeartbeatCollaborationInstructions(), contextInstructions);
3891
4609
  if (contextInstructions?.trim()) return joinPresentSections(buildDefaultCollaborationInstructions(), contextInstructions);
3892
4610
  return null;
3893
4611
  }
@@ -3914,15 +4632,8 @@ function buildCronCollaborationInstructions() {
3914
4632
  "Keep output concise and automation-oriented. Prefer the final command result or a short failure summary over status narration."
3915
4633
  ].join("\n\n");
3916
4634
  }
3917
- function buildHeartbeatCollaborationInstructions() {
3918
- return [
3919
- "This is an OpenClaw heartbeat turn. Apply these instructions only to this heartbeat wake; ordinary chat turns should stay in Codex Default mode.",
3920
- "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.",
3921
- GPT5_HEARTBEAT_PROMPT_OVERLAY
3922
- ].join("\n\n");
3923
- }
3924
4635
  function joinPresentSections(...sections) {
3925
4636
  return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
3926
4637
  }
3927
4638
  //#endregion
3928
- export { resolveCodexDynamicToolsLoadingForRuntime as A, buildCodexPluginThreadConfigInputFingerprint as B, codexLegacyDynamicToolsFingerprint as C, isSystemAgentOnlyCodexDynamicToolAllowlist as D, isForcedPrivateQaCodexRuntime as E, projectContextEngineAssemblyForCodex as F, shouldBuildCodexPluginThreadConfig as G, buildDisabledAppsConfigPatch as H, resolveCodexContextEngineProjectionMaxChars as I, isOpenAiCuratedMarketplace as J, ensureCodexPluginActivation as K, resolveCodexContextEngineProjectionReserveTokens as L, isContextEngineBindingCompatible as M, fitCodexProjectedContextForTurnStart as N, normalizeCodexDynamicToolName as O, neutralizeCodexExplicitMentionSigils as P, buildCodexPluginAppsConfigPatchFromPolicyContext as R, codexDynamicToolsFingerprint as S, filterCodexDynamicToolsForDisabledNativeSurface as T, buildPluginAppPolicyContext as U, buildCodexPluginThreadConfigTimeoutFallback as V, mergeCodexThreadConfigs as W, pluginReadParams as X, isOpenAiCuratedMarketplaceName as Y, resolveRecoverableCodexPluginConfigKeys as Z, resolveCodexAppServerThreadModelSelection as _, isCodexAppServerProfilerEnabled as a, readCodexSupportedReasoningEfforts as b, buildCodexRingZeroThreadConfigPatch as c, buildCodexNativeWebSearchThreadConfig as d, resolveCodexWebSearchPlan as f, resolveCodexAppServerRequestModelSelection as g, resolveCodexAppServerModelProvider as h, startOrResumeThread as i, buildContextEngineBinding as j, resolveCodexDynamicToolsLoading as k, buildCodexRuntimeThreadConfig as l, CODEX_NATIVE_PERSONALITY_NONE as m, buildTurnStartParams as n, assertCodexRestrictedToolSurfaceHasNoManagedHooks as o, buildDeveloperInstructions as p, refreshCodexPluginRuntimeState as q, buildCodexUserInput as r, attestCodexRestrictedToolSurfaceMcpServersDisabled as s, buildTurnCollaborationMode as t, readCodexInheritedMcpServerNames as u, resolveCodexBindingModelProviderFallback as v, filterCodexDynamicTools as w, areCodexDynamicToolFingerprintsCompatible as x, resolveReasoningEffort as y, buildCodexPluginThreadConfig as z };
4639
+ 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 };