@openclaw/codex 2026.8.1-beta.2 → 2026.9.1-beta.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 (60) hide show
  1. package/dist/api.js +147 -0
  2. package/dist/{attempt-client-cleanup-DMLXUfa8.js → attempt-client-cleanup-bfsmcQ6m.js} +21 -2
  3. package/dist/{binding-connection-I5Bo-AsX.js → binding-connection-BMvPX73o.js} +15 -3
  4. package/dist/{bounded-turn-qgZmzy_4.js → bounded-turn-B7iBUcuP.js} +22 -16
  5. package/dist/{capabilities-CybGI1oz.js → capabilities-BRMh0aIq.js} +1 -1
  6. package/dist/cli-metadata.js +1 -1
  7. package/dist/{command-formatters-B1AEz8ZA.js → command-formatters-DEsIufkt.js} +2 -2
  8. package/dist/{command-handlers-Db8pu_ds.js → command-handlers-D_-1_qRp.js} +65 -37
  9. package/dist/command-rpc-CCeCkf_v.js +90 -0
  10. package/dist/{compact-LspQaGfl.js → compact-BglNFZrQ.js} +35 -29
  11. package/dist/{session-binding-DPxIvMgE.js → config-giW8X0QA.js} +108 -914
  12. package/dist/context-compaction-activity-BOeJf7yo.js +58 -0
  13. package/dist/doctor-contract-api.js +23 -21
  14. package/dist/{dynamic-tools-BVdyWkUD.js → dynamic-tools-Dnlfa--w.js} +1345 -1424
  15. package/dist/{effective-mcp-catalog-DIIhAtOZ.js → effective-mcp-catalog-CTeGrnfO.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-Bk1I90Bg.js} +18 -18
  19. package/dist/harness-Csz0F-DK.js +258 -0
  20. package/dist/harness.js +2 -226
  21. package/dist/index.js +262 -181
  22. package/dist/{isolated-completion-C58NbL40.js → isolated-completion-Cv6FqygM.js} +4 -4
  23. package/dist/managed-binary-DFYK4DWq.js +248 -0
  24. package/dist/media-understanding-provider.js +2 -2
  25. package/dist/model-catalog-i9rNBthX.js +48 -0
  26. package/dist/{models-BFlpn1Ex.js → models-TS3HBU-q.js} +4 -3
  27. package/dist/{native-subagent-monitor-DyTGmTUx.js → native-subagent-monitor-4Mfq2HDs.js} +354 -83
  28. package/dist/node-exec-server.runtime-BpWqJO1V.js +192 -0
  29. package/dist/{plugin-app-cache-key-C3H2CVp6.js → plugin-app-cache-key-C1CAdfcP.js} +19 -4
  30. package/dist/plugin-approval-roundtrip-bUm777tR.js +95 -0
  31. package/dist/{plugin-marketplace-discovery-CzrGJdCR.js → plugin-marketplace-discovery-B9DvaAG-.js} +59 -54
  32. package/dist/{plugin-metadata-cache-CNJPj4_b.js → plugin-metadata-cache-CTCT3w5L.js} +164 -134
  33. package/dist/{protocol-validators-B4F7L8Rf.js → protocol-validators-VeJxxJ0U.js} +80 -5
  34. package/dist/{rate-limits-DZXmOL8-.js → rate-limits-C5_D8Gmb.js} +1 -1
  35. package/dist/{request-BRl5_UfX.js → request-DXXGHko0.js} +2 -2
  36. package/dist/{run-attempt-P8BdxC1I.js → run-attempt-DYc0QCBF.js} +1276 -765
  37. package/dist/{runtime-artifact-BVSPdd6g.js → runtime-artifact-BvyT1uyd.js} +4 -7
  38. package/dist/{sandbox-guard-Cmwc9eIk.js → sandbox-guard-kuWTn9C5.js} +25 -19
  39. package/dist/session-binding-Bcynfd-_.js +874 -0
  40. package/dist/{session-catalog-xwNi_SDM.js → session-catalog-DaLKVtpH.js} +1534 -1288
  41. package/dist/session-catalog-home-id-BNwAJ9D7.js +18 -0
  42. package/dist/{session-cli-7hgXZDV7.js → session-cli-DMc4oS2w.js} +20 -7
  43. package/dist/session-permission-policy-BrNH1bjy.js +160 -0
  44. package/dist/{session-retirement-DoMMJFKm.js → session-retirement-CcNC3YSY.js} +3 -3
  45. package/dist/{session-upstream-marker-CrIFX06J.js → session-upstream-marker-BIkusb95.js} +1 -1
  46. package/dist/{settled-turn-finalizer-BRWBO4MO.js → settled-turn-finalizer-Dc3ApjU4.js} +24 -20
  47. package/dist/{shared-client-kIBbGjk4.js → shared-client-DKz23hHY.js} +1632 -372
  48. package/dist/{side-question-9HAL26l9.js → side-question-DjA_jsNe.js} +105 -54
  49. package/dist/{thread-lifecycle-Bu1BTDXr.js → thread-lifecycle-L7oEkTlq.js} +1147 -114
  50. package/dist/{thread-ownership-6kOQHlyC.js → thread-ownership-DZeuKH1j.js} +2 -2
  51. package/dist/{thread-resume-CPrJNQVM.js → thread-resume-BE9qNEW0.js} +3 -3
  52. package/dist/{transcript-mirror-B9gGgnJS.js → transcript-mirror-DsCMWBca.js} +137 -38
  53. package/dist/{transport-stdio-DIImwDVS.js → transport-stdio-DhoGy33Z.js} +2 -2
  54. package/dist/{upstream-prompt-provenance-ClRBije0.js → upstream-prompt-provenance-CFmsKwr9.js} +12 -14
  55. package/dist/{upstream-session-fork-CE-_LdnP.js → upstream-session-fork-DlaHxSLC.js} +12 -6
  56. package/dist/{usage-CHlV0e-o.js → usage-ROIaW3wo.js} +3 -3
  57. package/dist/{web-search-provider.runtime-DRw2Vl42.js → web-search-provider.runtime-ClaNPV7N.js} +3 -3
  58. package/openclaw.plugin.json +49 -2
  59. package/package.json +9 -8
  60. package/dist/app-server-policy-zcBDc1JU.js +0 -49
@@ -1,27 +1,35 @@
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";
1
+ import { _ as CODEX_PLUGINS_MARKETPLACE_NAME, b as assertCodexModelBackedReviewerEffectiveConfig, g as resolveCodexPluginsPolicy, n as codexSandboxPolicyForTurn, v as CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME } from "./config-giW8X0QA.js";
2
+ import { G as CodexAppServerRpcError, J as consumeCodexAppServerLiveThread, L as isCodexAppServerConnectionClosedError, P as getCodexAppServerClientInstanceId, St as resolveCodexAppServerLocalHomeDir, U as resolveCodexAppServerClientInstanceId, _t as flattenCodexDynamicToolFunctions, c as isCodexAppServerStartSelectionChangedError, h as retainSharedCodexAppServerClientByInstanceId, vt as isJsonObject } from "./shared-client-DKz23hHY.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-Bcynfd-_.js";
4
+ import { a as closeCodexStartupClientBestEffort, c as isCodexAppServerUnsafeSubscriptionError, d as unsubscribeCodexThreadBestEffort, n as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, r as CodexAppServerUnsafeSubscriptionError } from "./attempt-client-cleanup-bfsmcQ6m.js";
5
+ import { a as assertCodexThreadStartResponse, r as assertCodexThreadForkResponse } from "./protocol-validators-VeJxxJ0U.js";
6
+ import { o as defaultCodexAppInventoryCache, s as serializeCodexAppInventoryError, t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-C1CAdfcP.js";
6
7
  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";
8
+ import { n as codexCatalogHomeId } from "./session-catalog-home-id-BNwAJ9D7.js";
9
+ import { t as resumeCodexAppServerThread } from "./thread-resume-BE9qNEW0.js";
10
+ import { u as projectBoundedCodexThreadHistory } from "./transcript-mirror-DsCMWBca.js";
9
11
  import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-CnfoUPCE.js";
10
12
  import { asOptionalRecord, isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
11
13
  import fs from "node:fs/promises";
12
14
  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";
15
+ import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
16
+ import { addTimerTimeoutGraceMs, finiteSecondsToTimerSafeMilliseconds, parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime";
17
+ import { SKILL_WORKSHOP_TOOL_NAME, TRANSCRIPT_CREDENTIAL_SAFETY_PROMPT, buildDelegationGuidanceSection, buildHarnessVisibleReplyGuidance, buildSkillWorkshopPromptSection, embeddedAgentLog, formatErrorMessage, formatToolExecutionErrorMessage, isActiveHarnessContextEngine, isHostScopedAgentToolActive, resolveMainSessionDelegationMode, resolveToolExecutionErrorKind } from "openclaw/plugin-sdk/agent-harness-runtime";
14
18
  import * as crypto$1 from "node:crypto";
15
- import crypto from "node:crypto";
19
+ import crypto, { createHash } from "node:crypto";
16
20
  import os from "node:os";
21
+ import { resolveAgentDir as resolveAgentDir$1, resolveSessionAgentIds as resolveSessionAgentIds$1 } from "openclaw/plugin-sdk/agent-runtime";
22
+ import { z } from "zod";
17
23
  import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
18
24
  import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
19
25
  import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
26
+ import { isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
20
27
  import { resolveRequiredHomeDir, resolveStateDir } from "openclaw/plugin-sdk/state-paths";
21
28
  import { listRegisteredPluginAgentPromptGuidance } from "openclaw/plugin-sdk/plugin-runtime";
29
+ import { buildHostnameAllowlistPolicyFromSuffixAllowlist } from "openclaw/plugin-sdk/ssrf-policy";
22
30
  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";
31
+ import { emitTrustedDiagnosticEvent, hasPendingInternalDiagnosticEvent, isDiagnosticFlagEnabled } from "openclaw/plugin-sdk/diagnostic-runtime";
32
+ import { registerRetainedNativeHookRelayForBundledRuntime } from "openclaw/plugin-sdk/native-hook-relay-runtime";
25
33
  //#region extensions/codex/src/app-server/plugin-inventory.ts
26
34
  /**
27
35
  * Reads Codex plugin marketplace state and app inventory to decide which
@@ -451,12 +459,21 @@ function activationFailure(identity, reason, diagnostic, extraDiagnostics = [])
451
459
  }
452
460
  //#endregion
453
461
  //#region extensions/codex/src/app-server/plugin-thread-app-admission.ts
462
+ function resolveCodexPluginThreadAppCacheKey(params) {
463
+ return params.threadId ? `${params.appCacheKey}:thread:${encodeURIComponent(params.threadId)}` : params.appCacheKey;
464
+ }
465
+ function createCodexPluginThreadAppInventoryRequest(params) {
466
+ return async (method, requestParams) => await params.request(method, (method === "app/installed" || method === "app/read") && params.threadId ? {
467
+ ...requestParams,
468
+ threadId: params.threadId
469
+ } : requestParams);
470
+ }
454
471
  async function refreshCodexPluginAppInventory(params, appCache, options = {}) {
455
472
  if (!params.appCacheKey) return;
456
- const request = async (method, requestParams) => await params.request(method, requestParams);
473
+ const request = createCodexPluginThreadAppInventoryRequest(params);
457
474
  try {
458
475
  return await appCache.refreshNow({
459
- key: params.appCacheKey,
476
+ key: resolveCodexPluginThreadAppCacheKey(params),
460
477
  request,
461
478
  nowMs: params.nowMs,
462
479
  forceRefetch: options.forceRefetch,
@@ -493,9 +510,9 @@ function normalizeCodexPluginOwnerName(name) {
493
510
  return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
494
511
  }
495
512
  async function readCodexThreadAdmissibleAccountApps(params, appCache) {
496
- const request = async (method, requestParams) => await params.request(method, requestParams);
513
+ const request = createCodexPluginThreadAppInventoryRequest(params);
497
514
  const cachedInventory = appCache.read({
498
- key: params.appCacheKey,
515
+ key: resolveCodexPluginThreadAppCacheKey(params),
499
516
  request,
500
517
  nowMs: params.nowMs,
501
518
  suppressRefresh: true
@@ -610,6 +627,11 @@ function buildCodexPluginThreadConfigTimeoutFallback(params) {
610
627
  /** Builds the Codex apps config patch and policy context for a native thread. */
611
628
  async function buildCodexPluginThreadConfig(params) {
612
629
  const appCache = params.appCache ?? defaultCodexAppInventoryCache;
630
+ const threadAppCacheKey = resolveCodexPluginThreadAppCacheKey(params);
631
+ const threadRequest = (method, requestParams) => params.request(method, (method === "app/installed" || method === "app/read") && params.threadId && isJsonObject(requestParams) ? {
632
+ ...requestParams,
633
+ threadId: params.threadId
634
+ } : requestParams);
613
635
  let inputFingerprint = buildCodexPluginThreadConfigInputFingerprint({
614
636
  pluginConfig: params.pluginConfig,
615
637
  appCacheKey: params.appCacheKey
@@ -623,9 +645,9 @@ async function buildCodexPluginThreadConfig(params) {
623
645
  let inventory = policy.pluginPolicies.length > 0 ? await readCodexPluginInventory({
624
646
  pluginConfig: params.pluginConfig,
625
647
  policy,
626
- request: params.request,
648
+ request: threadRequest,
627
649
  appCache,
628
- appCacheKey: params.appCacheKey,
650
+ appCacheKey: threadAppCacheKey,
629
651
  configCwd: params.configCwd,
630
652
  metadataCache: params.metadataCache,
631
653
  nowMs: params.nowMs,
@@ -641,9 +663,9 @@ async function buildCodexPluginThreadConfig(params) {
641
663
  inventory = await readCodexPluginInventory({
642
664
  pluginConfig: params.pluginConfig,
643
665
  policy,
644
- request: params.request,
666
+ request: threadRequest,
645
667
  appCache,
646
- appCacheKey: params.appCacheKey,
668
+ appCacheKey: threadAppCacheKey,
647
669
  configCwd: params.configCwd,
648
670
  metadataCache: params.metadataCache,
649
671
  nowMs: params.nowMs
@@ -659,9 +681,9 @@ async function buildCodexPluginThreadConfig(params) {
659
681
  if (!record.activationRequired) continue;
660
682
  const activation = await ensureCodexPluginActivation({
661
683
  identity: record.policy,
662
- request: params.request,
684
+ request: threadRequest,
663
685
  appCache,
664
- appCacheKey: params.appCacheKey,
686
+ appCacheKey: threadAppCacheKey,
665
687
  configCwd: params.configCwd,
666
688
  metadataCache: params.metadataCache,
667
689
  deferAppInventoryRefresh: true,
@@ -685,9 +707,9 @@ async function buildCodexPluginThreadConfig(params) {
685
707
  inventory = await readCodexPluginInventory({
686
708
  pluginConfig: params.pluginConfig,
687
709
  policy,
688
- request: params.request,
710
+ request: threadRequest,
689
711
  appCache,
690
- appCacheKey: params.appCacheKey,
712
+ appCacheKey: threadAppCacheKey,
691
713
  configCwd: params.configCwd,
692
714
  metadataCache: params.metadataCache,
693
715
  nowMs: params.nowMs
@@ -706,9 +728,9 @@ async function buildCodexPluginThreadConfig(params) {
706
728
  inventory = await readCodexPluginInventory({
707
729
  pluginConfig: params.pluginConfig,
708
730
  policy,
709
- request: params.request,
731
+ request: threadRequest,
710
732
  appCache,
711
- appCacheKey: params.appCacheKey,
733
+ appCacheKey: threadAppCacheKey,
712
734
  configCwd: params.configCwd,
713
735
  metadataCache: params.metadataCache,
714
736
  nowMs: params.nowMs
@@ -1063,6 +1085,32 @@ function resolveCodexContextEngineProjectionMaxChars(params) {
1063
1085
  function resolveCodexContextEngineProjectionReserveTokens() {
1064
1086
  return DEFAULT_CODEX_PROJECTION_RESERVE_TOKENS;
1065
1087
  }
1088
+ const CONTINUITY_PROJECTION_RESERVE_RATIO = .5;
1089
+ const CONTINUITY_EMPIRICAL_CHARS_PER_TOKEN = 3;
1090
+ const CONTINUITY_MIN_CHARS_PER_TOKEN = .5;
1091
+ const CONTINUITY_MAX_CHARS_PER_TOKEN = CONTINUITY_EMPIRICAL_CHARS_PER_TOKEN;
1092
+ const CONTINUITY_CALIBRATION_MIN_PROMPT_CHARS = 5e4;
1093
+ /** Builds a calibration sample from a completed turn, or undefined if unusable. */
1094
+ function buildCodexContinuityCalibration(params) {
1095
+ if (!Number.isFinite(params.promptChars) || !Number.isFinite(params.inputTokens) || params.promptChars < CONTINUITY_CALIBRATION_MIN_PROMPT_CHARS || params.inputTokens <= 0) return;
1096
+ return {
1097
+ promptChars: Math.floor(params.promptChars),
1098
+ inputTokens: Math.floor(params.inputTokens)
1099
+ };
1100
+ }
1101
+ function resolveContinuityCharsPerToken(calibration) {
1102
+ 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;
1103
+ return Math.min(CONTINUITY_MAX_CHARS_PER_TOKEN, Math.max(CONTINUITY_MIN_CHARS_PER_TOKEN, calibration.promptChars / calibration.inputTokens));
1104
+ }
1105
+ /** Resolves rendered context size for no-engine continuity projections. */
1106
+ function resolveCodexContinuityProjectionMaxChars(params) {
1107
+ const contextTokenBudget = typeof params.contextTokenBudget === "number" && Number.isFinite(params.contextTokenBudget) ? Math.floor(params.contextTokenBudget) : void 0;
1108
+ if (!contextTokenBudget || contextTokenBudget <= 0) return DEFAULT_RENDERED_CONTEXT_CHARS;
1109
+ return normalizeRenderedContextMaxChars(resolveProjectionPromptBudgetTokens({
1110
+ contextTokenBudget,
1111
+ reserveTokens: Math.max(DEFAULT_CODEX_PROJECTION_RESERVE_TOKENS, Math.floor(contextTokenBudget * CONTINUITY_PROJECTION_RESERVE_RATIO))
1112
+ }) * resolveContinuityCharsPerToken(params.calibration));
1113
+ }
1066
1114
  /** Fits projected context prompts under Codex app-server turn/start text limits. */
1067
1115
  function fitCodexProjectedContextForTurnStart(params) {
1068
1116
  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 +1376,6 @@ const CODEX_APP_SERVER_OWNED_REPLACEABLE_TOOL_EXCLUDES = /* @__PURE__ */ new Set
1328
1376
  "write",
1329
1377
  "edit",
1330
1378
  "apply_patch",
1331
- "update_plan",
1332
1379
  ...CODEX_NATIVE_GOAL_TOOL_EXCLUDES
1333
1380
  ]);
1334
1381
  const CODEX_APP_SERVER_OWNED_SHELL_TOOL_EXCLUDES = /* @__PURE__ */ new Set(["exec", "process"]);
@@ -1561,10 +1608,6 @@ const nativeSkillIsolationByClient = /* @__PURE__ */ new WeakMap();
1561
1608
  function isMissingPathError(error) {
1562
1609
  return error.code === "ENOENT";
1563
1610
  }
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
1611
  async function canonicalizeExistingPath(candidate) {
1569
1612
  try {
1570
1613
  return await fs.realpath(candidate);
@@ -1590,12 +1633,12 @@ async function collectPersonalSkillRealPaths(homes, codexHome) {
1590
1633
  const realDefaultCodexHome = await canonicalizeExistingPath(defaultCodexHome);
1591
1634
  roots.push({
1592
1635
  dir: path.join(defaultCodexHome, "skills"),
1593
- onlyEscapedStateTargets: isPathWithin(realStateDir, realDefaultCodexHome)
1636
+ onlyEscapedStateTargets: isPathInside(realStateDir, realDefaultCodexHome)
1594
1637
  });
1595
1638
  }
1596
1639
  const configuredCodexHome = codexHome?.trim() || process.env.CODEX_HOME?.trim();
1597
1640
  if (configuredCodexHome) {
1598
- const stateOwned = isPathWithin(realStateDir, await canonicalizeExistingPath(configuredCodexHome));
1641
+ const stateOwned = isPathInside(realStateDir, await canonicalizeExistingPath(configuredCodexHome));
1599
1642
  roots.push({
1600
1643
  dir: path.join(configuredCodexHome, "skills"),
1601
1644
  onlyEscapedStateTargets: stateOwned
@@ -1613,7 +1656,7 @@ async function collectPersonalSkillRealPaths(homes, codexHome) {
1613
1656
  const recordSkillFile = async (filePath, onlyEscapedStateTargets) => {
1614
1657
  try {
1615
1658
  const skillRealPath = await fs.realpath(filePath);
1616
- if (!onlyEscapedStateTargets || !isPathWithin(realStateDir, skillRealPath)) skillPaths.add(skillRealPath);
1659
+ if (!onlyEscapedStateTargets || !isPathInside(realStateDir, skillRealPath)) skillPaths.add(skillRealPath);
1617
1660
  } catch (error) {
1618
1661
  if (!isMissingPathError(error)) complete = false;
1619
1662
  }
@@ -1756,7 +1799,7 @@ function applyCodexNativeSkillIsolation(config, isolation) {
1756
1799
  //#region extensions/codex/src/app-server/thread-lifecycle-errors.ts
1757
1800
  var CodexThreadStartRequestError = class extends Error {
1758
1801
  constructor(cause) {
1759
- super(formatErrorMessage(cause), { cause });
1802
+ super(`thread/start: ${formatErrorMessage(cause)}`, { cause });
1760
1803
  this.name = "CodexThreadStartRequestError";
1761
1804
  }
1762
1805
  };
@@ -1780,6 +1823,64 @@ var CodexAdoptedThreadActiveError = class extends Error {
1780
1823
  }
1781
1824
  };
1782
1825
  //#endregion
1826
+ //#region extensions/codex/src/app-server/managed-thread-store.ts
1827
+ const CODEX_MANAGED_THREAD_NAMESPACE = "app-server-managed-threads";
1828
+ const CODEX_MANAGED_THREAD_MAX_ENTRIES = 2e4;
1829
+ const managedThreadSchema = z.object({
1830
+ version: z.literal(1),
1831
+ kind: z.literal("managed-thread"),
1832
+ sourceHomeId: z.string().min(1),
1833
+ threadId: z.string().min(1),
1834
+ rolloutPath: z.string().min(1).optional()
1835
+ });
1836
+ async function markStartedCodexManagedThread(store, params) {
1837
+ if (!store) return;
1838
+ try {
1839
+ await store.mark({
1840
+ sourceHomeId: params.sourceHomeId,
1841
+ threadId: params.threadId,
1842
+ ...params.rolloutPath ? { rolloutPath: params.rolloutPath } : {}
1843
+ });
1844
+ } catch (error) {
1845
+ embeddedAgentLog.warn("failed to record Codex managed thread ownership", { error });
1846
+ }
1847
+ }
1848
+ function managedThreadStoreKey(sourceHomeId, threadId) {
1849
+ return `sha256:${createHash("sha256").update("openclaw:codex-managed-thread:v1\0").update(sourceHomeId).update("\0").update(threadId).digest("hex")}`;
1850
+ }
1851
+ /** Durable ownership index for Codex threads created by OpenClaw. */
1852
+ function createCodexManagedThreadStore(state) {
1853
+ return {
1854
+ async mark(params) {
1855
+ try {
1856
+ const value = managedThreadSchema.parse({
1857
+ version: 1,
1858
+ kind: "managed-thread",
1859
+ sourceHomeId: params.sourceHomeId.trim(),
1860
+ threadId: params.threadId.trim(),
1861
+ ...params.rolloutPath?.trim() ? { rolloutPath: params.rolloutPath.trim() } : {}
1862
+ });
1863
+ state.registerIfAbsent(managedThreadStoreKey(value.sourceHomeId, value.threadId), value);
1864
+ return true;
1865
+ } catch (error) {
1866
+ embeddedAgentLog.warn("failed to record Codex managed thread ownership", { error });
1867
+ return false;
1868
+ }
1869
+ },
1870
+ async snapshot() {
1871
+ const byHome = /* @__PURE__ */ new Map();
1872
+ for (const entry of state.entries()) {
1873
+ const parsed = managedThreadSchema.safeParse(entry.value);
1874
+ if (!parsed.success) continue;
1875
+ const ids = byHome.get(parsed.data.sourceHomeId) ?? /* @__PURE__ */ new Set();
1876
+ ids.add(parsed.data.threadId);
1877
+ byHome.set(parsed.data.sourceHomeId, ids);
1878
+ }
1879
+ return byHome;
1880
+ }
1881
+ };
1882
+ }
1883
+ //#endregion
1783
1884
  //#region extensions/codex/src/app-server/plugin-thread-attestation.ts
1784
1885
  /**
1785
1886
  * Confirms admitted plugin and account apps against their actual Codex thread before
@@ -1835,6 +1936,50 @@ async function discardUnattestedCodexPluginThread(params) {
1835
1936
  }
1836
1937
  }
1837
1938
  //#endregion
1939
+ //#region extensions/codex/src/app-server/thread-lifecycle-result.ts
1940
+ /** Materializes the public lifecycle result after a fresh thread is durably committed. */
1941
+ function buildStartedCodexThreadBinding(input) {
1942
+ const { context, params, response, startParams } = input;
1943
+ return {
1944
+ threadId: response.thread.id,
1945
+ ...input.clientId ? { clientId: input.clientId } : {},
1946
+ cwd: params.cwd,
1947
+ ...input.rolloutPath ? { rolloutPath: input.rolloutPath } : {},
1948
+ authProfileId: params.params.authProfileId,
1949
+ agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
1950
+ model: response.model ?? startParams.model ?? params.params.modelId,
1951
+ modelProvider: response.modelProvider ?? input.startModelProvider ?? input.modelProvider,
1952
+ dynamicToolsFingerprint: context.dynamicToolsFingerprint,
1953
+ dynamicToolsContainDeferred: context.dynamicToolsContainDeferred,
1954
+ nativeSkillIsolationFingerprint: context.nativeSkillIsolationFingerprint,
1955
+ userMcpServersFingerprint: context.userMcpServersFingerprint,
1956
+ mcpServersFingerprint: input.nextMcpServersFingerprint,
1957
+ configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
1958
+ ringZeroConfigFingerprint: context.ringZeroConfigFingerprint,
1959
+ ringZeroClientInstanceId: context.ringZeroClientInstanceId,
1960
+ networkProxyProfileName: params.appServer.networkProxy?.profileName,
1961
+ networkProxyConfigFingerprint: context.networkProxyConfigFingerprint,
1962
+ nativeHookRelayGeneration: input.finalConfigPatch.nativeHookRelayGeneration,
1963
+ appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
1964
+ pluginAppsFingerprint: input.pluginThreadConfig?.fingerprint,
1965
+ pluginAppsInputFingerprint: input.pluginThreadConfig?.inputFingerprint,
1966
+ pluginAppPolicyContext: input.pluginThreadConfig?.policyContext,
1967
+ contextEngine: context.contextEngineBinding,
1968
+ environmentSelectionFingerprint: context.environmentSelectionFingerprint,
1969
+ ...!context.preserveExistingBinding ? { liveThreadConfigFingerprint: fingerprintCodexThreadConfig({
1970
+ ...startParams,
1971
+ model: response.model ?? startParams.model ?? null,
1972
+ requestedModel: startParams.model ?? null,
1973
+ modelProvider: input.bindingModelProvider ?? null,
1974
+ requestedModelProvider: startParams.modelProvider ?? input.bindingModelProvider ?? null
1975
+ }, params.params.authProfileId, context.dynamicToolsFingerprint) } : {},
1976
+ lifecycle: {
1977
+ action: "started",
1978
+ ...context.rotatedContextEngineBinding ? { rotatedContextEngineBinding: true } : {}
1979
+ }
1980
+ };
1981
+ }
1982
+ //#endregion
1838
1983
  //#region extensions/codex/src/app-server/reasoning-effort.ts
1839
1984
  const CODEX_REASONING_EFFORTS = [
1840
1985
  "minimal",
@@ -1963,12 +2108,21 @@ function resolveReasoningEffort(thinkLevel, modelId, supportedReasoningEfforts)
1963
2108
  });
1964
2109
  }
1965
2110
  //#endregion
2111
+ //#region extensions/codex/src/app-server/project-doc-thread-config.ts
2112
+ const CODEX_NATIVE_PROJECT_DOC_MAX_BYTES = 128 * 1024;
2113
+ function buildCodexProjectDocThreadConfig(config) {
2114
+ const defaults = { project_doc_max_bytes: CODEX_NATIVE_PROJECT_DOC_MAX_BYTES };
2115
+ return mergeCodexThreadConfigs(defaults, config) ?? defaults;
2116
+ }
2117
+ //#endregion
1966
2118
  //#region extensions/codex/src/app-server/thread-prompt.ts
1967
2119
  function buildDeveloperInstructions(params, options = {}) {
1968
2120
  const deferredToolNames = /* @__PURE__ */ new Set();
1969
2121
  let hasSkillWorkshop = false;
1970
2122
  let hasSessionsSpawn = false;
1971
2123
  let hasSessionsYield = false;
2124
+ let hasSubagentsList = false;
2125
+ let hasSessionsSend = false;
1972
2126
  let hasSeenDirectNamespace = false;
1973
2127
  let messageToolAvailable = options.dynamicTools ? false : params.disableMessageTool !== true;
1974
2128
  for (const spec of options.dynamicTools ?? []) {
@@ -1980,6 +2134,8 @@ function buildDeveloperInstructions(params, options = {}) {
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),
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
+ }),
2000
2173
  TRANSCRIPT_CREDENTIAL_SAFETY_PROMPT,
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,29 +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 = mergeCodexThreadConfigs(configured, CODEX_CODE_MODE_DISABLED_THREAD_CONFIG, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG) ?? {
2472
+ ...CODEX_CODE_MODE_DISABLED_THREAD_CONFIG,
2473
+ ...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG,
2474
+ ...CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG
2475
+ };
2260
2476
  delete disabledConfig["features.apply_patch_streaming_events"];
2261
2477
  return disabledConfig;
2262
2478
  }
2263
- if (options.nativeCodeModeOnlyEnabled === true) return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, config, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, { "features.code_mode_only": true }) ?? {
2479
+ if (options.nativeCodeModeOnlyEnabled === true) return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, configured, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG, { "features.code_mode_only": true }) ?? {
2264
2480
  ...codeModeConfig,
2265
2481
  ...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG,
2482
+ ...CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG,
2266
2483
  "features.code_mode_only": true
2267
2484
  }, options.directOnlyToolNamespaces);
2268
- return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, config, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG) ?? {
2485
+ return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, configured, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG) ?? {
2269
2486
  ...codeModeConfig,
2270
- ...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG
2487
+ ...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG,
2488
+ ...CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG
2271
2489
  }, options.directOnlyToolNamespaces);
2272
2490
  }
2273
2491
  function ensureDirectOnlyToolNamespaces(config, requiredNamespaces) {
@@ -2286,6 +2504,7 @@ function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
2286
2504
  const ringZeroActive = (options.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow);
2287
2505
  const messageOnlySourceReply = isMessageOnlyCodexSourceReply(params);
2288
2506
  const restrictedToolSurface = ringZeroActive || messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted === true;
2507
+ const restrictedTurnDisablesProjectDocs = ringZeroActive || messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted && params.disableTools;
2289
2508
  const configMcpServers = config?.mcp_servers;
2290
2509
  if (restrictedToolSurface && configMcpServers !== void 0 && !isJsonObject(configMcpServers)) throw new Error("Codex restricted tool surface received invalid thread mcp_servers config");
2291
2510
  const restrictedToolSurfaceMcpServerNames = [...options.restrictedToolSurfaceInheritedMcpServerNames ?? [], ...isJsonObject(configMcpServers) ? Object.keys(configMcpServers) : []];
@@ -2301,18 +2520,19 @@ function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
2301
2520
  webSearchAllowed: options.webSearchAllowed
2302
2521
  }).threadConfig;
2303
2522
  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
- };
2523
+ return applyCodexManagedShellEnvironment({
2524
+ ...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,
2525
+ ...params.bootstrapContextMode === "lightweight" ? CODEX_NO_PROJECT_DOCS_CONFIG : {}
2526
+ }, options.shellEnvironment, options.disableLoginShell);
2310
2527
  }
2311
2528
  function buildCodexRingZeroThreadConfigPatch(params, hostSystemAgentActive = isHostScopedAgentToolActive("openclaw"), inheritedMcpServerNames = []) {
2312
2529
  if (!hostSystemAgentActive || !isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow)) return;
2313
- return buildCodexRestrictedToolThreadConfigPatch(inheritedMcpServerNames);
2530
+ return {
2531
+ ...buildRestrictedToolConfigPatch(inheritedMcpServerNames),
2532
+ ...CODEX_NO_PROJECT_DOCS_CONFIG
2533
+ };
2314
2534
  }
2315
- function buildCodexRestrictedToolThreadConfigPatch(inheritedMcpServerNames) {
2535
+ function buildRestrictedToolConfigPatch(inheritedMcpServerNames) {
2316
2536
  const mcpServers = Object.fromEntries([...new Set(inheritedMcpServerNames)].toSorted().map((name) => [name, { enabled: false }]));
2317
2537
  return {
2318
2538
  ...CODEX_RING_ZERO_THREAD_CONFIG,
@@ -2336,12 +2556,12 @@ async function readCodexInheritedMcpServerNames(client, cwd, signal) {
2336
2556
  if (!isJsonObject(configuredServers)) throw new Error("Codex config/read returned invalid mcp_servers");
2337
2557
  return Object.keys(configuredServers).toSorted();
2338
2558
  }
2339
- async function assertCodexRestrictedToolSurfaceHasNoManagedHooks(client, signal) {
2559
+ async function assertCodexManagedRequirementsDoNotOverrideToolPolicy(client, options, signal) {
2340
2560
  const response = await client.request("configRequirements/read", void 0, { signal });
2341
2561
  if (!isJsonObject(response) || !Object.hasOwn(response, "requirements")) throw new Error("Codex configRequirements/read returned an invalid response");
2342
2562
  if (response.requirements === null) return;
2343
2563
  if (!isJsonObject(response.requirements)) throw new Error("Codex configRequirements/read returned invalid requirements");
2344
- for (const key of [
2564
+ if (options.restrictedToolSurface) for (const key of [
2345
2565
  "hooks",
2346
2566
  "managedHooks",
2347
2567
  "managed_hooks"
@@ -2351,6 +2571,7 @@ async function assertCodexRestrictedToolSurfaceHasNoManagedHooks(client, signal)
2351
2571
  if (!isJsonObject(hooks)) throw new Error("Codex configRequirements/read returned invalid managed hooks");
2352
2572
  if (hasNonEmptyJsonValue(hooks)) throw new Error("Codex restricted tool surface cannot override managed hooks");
2353
2573
  }
2574
+ const additionalDeniedFeatures = new Set(options.additionalDeniedFeatures);
2354
2575
  for (const key of ["featureRequirements", "feature_requirements"]) {
2355
2576
  const requirements = response.requirements[key];
2356
2577
  if (requirements === void 0 || requirements === null) continue;
@@ -2358,7 +2579,8 @@ async function assertCodexRestrictedToolSurfaceHasNoManagedHooks(client, signal)
2358
2579
  for (const [feature, enabled] of Object.entries(requirements)) {
2359
2580
  if (typeof enabled !== "boolean") throw new Error("Codex configRequirements/read returned invalid feature requirements");
2360
2581
  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}`);
2582
+ const deniedByToolPolicy = options.restrictedToolSurface && CODEX_RING_ZERO_RESTRICTED_FEATURES.has(canonicalFeature) || additionalDeniedFeatures.has(canonicalFeature);
2583
+ if (enabled && deniedByToolPolicy) throw new Error(`Codex tool policy cannot override required feature ${feature}`);
2362
2584
  }
2363
2585
  }
2364
2586
  }
@@ -2408,6 +2630,15 @@ function resolveCodexThreadEnvironmentSelection(options) {
2408
2630
  }
2409
2631
  //#endregion
2410
2632
  //#region extensions/codex/src/app-server/thread-lifecycle-io.ts
2633
+ function resolveManagedThreadSourceHomeId(params) {
2634
+ const agentId = resolveSessionAgentIds$1({
2635
+ config: params.params.config,
2636
+ sessionKey: params.params.sessionKey,
2637
+ agentId: params.agentId ?? params.params.agentId
2638
+ }).sessionAgentId;
2639
+ const agentDir = params.agentDir ?? params.params.agentDir ?? resolveAgentDir$1(params.params.config ?? {}, agentId);
2640
+ return codexCatalogHomeId(resolveCodexAppServerLocalHomeDir(params.appServer.start, agentDir));
2641
+ }
2411
2642
  function resolveCodexThreadRolloutPath(thread) {
2412
2643
  const rolloutPath = thread.path?.trim();
2413
2644
  if (!rolloutPath || !path.isAbsolute(rolloutPath) || path.extname(rolloutPath) !== ".jsonl" || !path.basename(rolloutPath).includes(thread.id)) return;
@@ -2429,6 +2660,7 @@ async function resumeExistingCodexThread(params, context) {
2429
2660
  const resumeConfig = applyCodexNativeSkillIsolation(mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, finalConfigPatch.configPatch), nativeSkillIsolation);
2430
2661
  const resumeParams = lifecycleTiming.measureSync("thread-resume-params", () => buildThreadResumeParams(params.params, {
2431
2662
  threadId: resumeBinding.threadId,
2663
+ cwd: params.cwd,
2432
2664
  authProfileId,
2433
2665
  model: startModelSelection.model,
2434
2666
  modelProvider: startModelProvider,
@@ -2442,7 +2674,9 @@ async function resumeExistingCodexThread(params, context) {
2442
2674
  nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
2443
2675
  webSearchAllowed: params.webSearchAllowed,
2444
2676
  hostSystemAgentActive,
2445
- restrictedToolSurfaceInheritedMcpServerNames
2677
+ restrictedToolSurfaceInheritedMcpServerNames,
2678
+ shellEnvironment: params.shellEnvironment,
2679
+ disableLoginShell: params.disableLoginShell
2446
2680
  }));
2447
2681
  const requestModelProvider = typeof resumeParams.modelProvider === "string" && resumeParams.modelProvider.trim() ? resumeParams.modelProvider : void 0;
2448
2682
  throwIfAborted();
@@ -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 = resolveManagedThreadSourceHomeId(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,44 +2959,820 @@ async function startFreshCodexThread(params, context) {
2716
2959
  threadId: response.thread.id,
2717
2960
  action: rotatedContextEngineBinding ? "rotated" : "started"
2718
2961
  });
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
+ });
2976
+ }
2977
+ //#endregion
2978
+ //#region extensions/codex/src/app-server/dynamic-tool-response-state.ts
2979
+ /** Retains the host-owned app preview without adding it to Codex's response payload. */
2980
+ function withDynamicToolTranscriptDetails(response, details) {
2981
+ if (details === void 0) return response;
2982
+ Object.defineProperty(response, "transcriptDetails", {
2983
+ configurable: true,
2984
+ enumerable: false,
2985
+ value: details
2986
+ });
2987
+ return response;
2988
+ }
2989
+ function withDynamicToolTerminalResolution(response, terminalResolution) {
2990
+ if (terminalResolution) {
2991
+ Object.defineProperties(response, {
2992
+ terminalResolution: {
2993
+ configurable: true,
2994
+ enumerable: false,
2995
+ value: terminalResolution
2996
+ },
2997
+ executionStarted: {
2998
+ configurable: true,
2999
+ enumerable: false,
3000
+ value: terminalResolution.executionStarted
3001
+ },
3002
+ ...terminalResolution.executedArguments ? { executedArguments: {
3003
+ configurable: true,
3004
+ enumerable: false,
3005
+ value: terminalResolution.executedArguments
3006
+ } } : {}
3007
+ });
3008
+ withDynamicToolSideEffectEvidence(response, terminalResolution.sideEffectEvidence);
3009
+ }
3010
+ return response;
3011
+ }
3012
+ function withDynamicToolExecutionState(response, state) {
3013
+ Object.defineProperties(response, {
3014
+ executedArguments: {
3015
+ configurable: true,
3016
+ enumerable: false,
3017
+ value: state.executedArguments
3018
+ },
3019
+ executionStarted: {
3020
+ configurable: true,
3021
+ enumerable: false,
3022
+ value: state.executionStarted
3023
+ }
3024
+ });
3025
+ return withDynamicToolSideEffectEvidence(response, state.sideEffectEvidence === true);
3026
+ }
3027
+ function withDynamicToolSideEffectEvidence(response, sideEffectEvidence) {
3028
+ if (!sideEffectEvidence) {
3029
+ delete response.sideEffectEvidence;
3030
+ return response;
3031
+ }
3032
+ Object.defineProperty(response, "sideEffectEvidence", {
3033
+ configurable: true,
3034
+ enumerable: false,
3035
+ value: true
3036
+ });
3037
+ return response;
3038
+ }
3039
+ function createFailedDynamicToolResponse(message, options) {
3040
+ const response = {
3041
+ contentItems: [{
3042
+ type: "inputText",
3043
+ text: message
3044
+ }],
3045
+ success: false
3046
+ };
3047
+ Object.defineProperties(response, {
3048
+ diagnosticTerminalReason: {
3049
+ configurable: true,
3050
+ enumerable: false,
3051
+ value: options?.terminalReason ?? "failed"
3052
+ },
3053
+ diagnosticTerminalType: {
3054
+ configurable: true,
3055
+ enumerable: false,
3056
+ value: "error"
3057
+ }
3058
+ });
3059
+ if (options?.executionStarted !== void 0) Object.defineProperty(response, "executionStarted", {
3060
+ configurable: true,
3061
+ enumerable: false,
3062
+ value: options.executionStarted
3063
+ });
3064
+ if (options?.executedArguments !== void 0) Object.defineProperty(response, "executedArguments", {
3065
+ configurable: true,
3066
+ enumerable: false,
3067
+ value: options.executedArguments
3068
+ });
3069
+ return withDynamicToolSideEffectEvidence(response, options?.sideEffectEvidence === true);
3070
+ }
3071
+ //#endregion
3072
+ //#region extensions/codex/src/app-server/tool-abort-terminal-reason.ts
3073
+ /** Leaf helper shared by native and dynamic tool diagnostics. */
3074
+ const CODEX_TIMEOUT_ABORT_REASONS = /* @__PURE__ */ new Set([
3075
+ "codex_startup_timeout",
3076
+ "turn_completion_idle_timeout",
3077
+ "turn_progress_idle_timeout",
3078
+ "turn_terminal_idle_timeout"
3079
+ ]);
3080
+ /** Preserves timeout provenance when an enclosing run aborts an active tool. */
3081
+ function resolveCodexToolAbortTerminalReason(signal) {
3082
+ try {
3083
+ const reason = signal.reason;
3084
+ if (typeof reason === "string") {
3085
+ if (CODEX_TIMEOUT_ABORT_REASONS.has(reason)) return "timed_out";
3086
+ return reason === "client_closed" ? "failed" : "cancelled";
3087
+ }
3088
+ if (reason && typeof reason === "object") {
3089
+ const record = reason;
3090
+ if (record.name === "TimeoutError" || record.reason === "timeout") return "timed_out";
3091
+ }
3092
+ } catch {
3093
+ return "cancelled";
3094
+ }
3095
+ return "cancelled";
3096
+ }
3097
+ //#endregion
3098
+ //#region extensions/codex/src/app-server/dynamic-tool-execution.ts
3099
+ /**
3100
+ * Timeout, terminal-release, and diagnostic helpers for Codex dynamic tool
3101
+ * calls.
3102
+ */
3103
+ /** Default timeout for Codex dynamic tool calls. */
3104
+ const CODEX_DYNAMIC_TOOL_TIMEOUT_MS = 9e4;
3105
+ /** Hard cap for per-call Codex dynamic tool timeout overrides. */
3106
+ const CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS = 6e5;
3107
+ const CODEX_DYNAMIC_TOOL_TIMEOUT_SECONDS_GRACE_MS = 3e4;
3108
+ const CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12e4;
3109
+ const CODEX_DYNAMIC_COMPUTER_GATEWAY_TIMEOUT_MS = 3e4;
3110
+ const CODEX_DYNAMIC_COMPUTER_COMPLETION_GRACE_MS = 3e4;
3111
+ /** Timeout for image-understanding style dynamic tool calls. */
3112
+ const CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS = 6e4;
3113
+ /** Timeout for message-delivery dynamic tool calls. */
3114
+ const CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS = 6e5;
3115
+ /** Outer default for collector waits: full swarm budget plus completion grace. */
3116
+ const CODEX_DYNAMIC_AGENTS_WAIT_TOOL_TIMEOUT_MS = 63e4;
3117
+ const LOG_FIELD_MAX_LENGTH = 160;
3118
+ function normalizeLogField(value) {
3119
+ if (typeof value !== "string") return;
3120
+ const normalized = value.replaceAll(String.fromCharCode(27), " ").replaceAll("\r", " ").replaceAll("\n", " ").replaceAll(" ", " ").trim();
3121
+ if (!normalized) return;
3122
+ return normalized.length > LOG_FIELD_MAX_LENGTH ? `${truncateUtf16Safe(normalized, LOG_FIELD_MAX_LENGTH - 3)}...` : normalized;
3123
+ }
3124
+ function readNumericTimeoutMs(value) {
3125
+ if (typeof value === "number" && Number.isFinite(value)) return Math.max(0, Math.floor(value));
3126
+ if (typeof value === "string") {
3127
+ const parsed = parseStrictNonNegativeInteger(value);
3128
+ if (parsed !== void 0) return Math.max(0, Math.floor(parsed));
3129
+ }
3130
+ }
3131
+ function formatDynamicToolTimeoutDetails(params) {
3132
+ const tool = normalizeLogField(params.call.tool) ?? "unknown";
3133
+ const baseMeta = {
3134
+ tool: params.call.tool,
3135
+ toolCallId: params.call.callId,
3136
+ threadId: params.call.threadId,
3137
+ turnId: params.call.turnId,
3138
+ timeoutMs: params.timeoutMs,
3139
+ timeoutKind: "codex_dynamic_tool_rpc"
3140
+ };
3141
+ if (tool !== "process" || !isJsonObject(params.call.arguments)) return {
3142
+ responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms while running tool ${tool}.`,
3143
+ consoleMessage: `codex dynamic tool timeout: tool=${tool} toolTimeoutMs=${params.timeoutMs}; per-tool-call watchdog, not session idle`,
3144
+ meta: baseMeta
3145
+ };
3146
+ const action = normalizeLogField(params.call.arguments.action);
3147
+ const sessionId = normalizeLogField(params.call.arguments.sessionId);
3148
+ const requestedTimeoutMs = readNumericTimeoutMs(params.call.arguments.timeout);
3149
+ const actionPart = action ? ` action=${action}` : "";
3150
+ const sessionPart = sessionId ? ` sessionId=${sessionId}` : "";
3151
+ const requestedPart = requestedTimeoutMs === void 0 ? "" : ` requestedWaitMs=${requestedTimeoutMs}`;
3152
+ const retryHint = action === "poll" ? "; repeated lines usually mean process-poll retry churn, not model progress" : "";
3153
+ const responseTarget = action || sessionId ? ` while waiting for process${actionPart}${sessionPart}` : " while waiting for the process tool";
2719
3154
  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 } : {}
3155
+ responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms${responseTarget}. This is a tool RPC timeout, not a session idle timeout.`,
3156
+ consoleMessage: `codex process tool timeout:${actionPart}${sessionPart} toolTimeoutMs=${params.timeoutMs}${requestedPart}; per-tool-call watchdog, not session idle${retryHint}`,
3157
+ meta: {
3158
+ ...baseMeta,
3159
+ processAction: action,
3160
+ processSessionId: sessionId,
3161
+ processRequestedTimeoutMs: requestedTimeoutMs
2754
3162
  }
2755
3163
  };
2756
3164
  }
3165
+ /**
3166
+ * Runs a dynamic tool call with run-abort and per-call timeout handling,
3167
+ * returning a Codex protocol response instead of throwing.
3168
+ */
3169
+ async function handleDynamicToolCallWithTimeout(params) {
3170
+ let didNotifyAgentToolResult = false;
3171
+ const conservativeRaceResponses = /* @__PURE__ */ new WeakSet();
3172
+ const finalizeTerminal = (response) => {
3173
+ const executionSnapshot = params.toolBridge.consumeToolExecutionSnapshot?.(params.call.callId);
3174
+ const ownerKey = params.toolBridge.sideEffectOwnerKeyForTool?.(params.call.tool);
3175
+ const observedExecutionStarted = executionSnapshot?.executionStarted ?? (conservativeRaceResponses.has(response) ? void 0 : response.executionStarted);
3176
+ const terminalResolution = params.observeToolTerminal?.({
3177
+ toolCallId: params.call.callId,
3178
+ toolName: params.call.tool,
3179
+ arguments: response.executedArguments ?? executionSnapshot?.executedArguments ?? params.call.arguments,
3180
+ ...params.toolMeta ? { meta: params.toolMeta } : {},
3181
+ ...ownerKey ? { ownerMutation: { ownerKey } } : {},
3182
+ ...observedExecutionStarted !== void 0 ? { executionStarted: observedExecutionStarted } : {},
3183
+ outcome: response.success ? "success" : "failure",
3184
+ ...!response.success ? { failure: { error: readDynamicToolResponseText(response) } } : {}
3185
+ });
3186
+ return withDynamicToolTerminalResolution(response, terminalResolution);
3187
+ };
3188
+ const createFailedAfterPossibleDispatch = (message, terminalReason) => {
3189
+ const response = createFailedDynamicToolResponse(message, {
3190
+ executionStarted: true,
3191
+ sideEffectEvidence: true,
3192
+ terminalReason
3193
+ });
3194
+ conservativeRaceResponses.add(response);
3195
+ return response;
3196
+ };
3197
+ const notifyAgentToolResult = (event) => {
3198
+ if (didNotifyAgentToolResult) return;
3199
+ didNotifyAgentToolResult = true;
3200
+ try {
3201
+ params.onAgentToolResult?.(event);
3202
+ } catch (error) {
3203
+ embeddedAgentLog.warn(`onAgentToolResult handler failed: tool=${params.call.tool} error=${String(error)}`);
3204
+ }
3205
+ };
3206
+ const notifyFailedToolResult = (message, terminalReason = "failed") => {
3207
+ notifyAgentToolResult({
3208
+ toolName: params.call.tool,
3209
+ result: {
3210
+ content: [{
3211
+ type: "text",
3212
+ text: message
3213
+ }],
3214
+ details: {
3215
+ status: terminalReason,
3216
+ error: message
3217
+ }
3218
+ },
3219
+ isError: true
3220
+ });
3221
+ };
3222
+ if (params.signal.aborted) {
3223
+ const message = "OpenClaw dynamic tool call aborted before execution.";
3224
+ const terminalReason = resolveCodexToolAbortTerminalReason(params.signal);
3225
+ params.onFallbackSelected?.();
3226
+ notifyFailedToolResult(message, terminalReason);
3227
+ return finalizeTerminal(createFailedDynamicToolResponse(message, {
3228
+ executionStarted: false,
3229
+ terminalReason
3230
+ }));
3231
+ }
3232
+ const controller = new AbortController();
3233
+ let timeout;
3234
+ let timedOut = false;
3235
+ let resolveAbort;
3236
+ const abortFromRun = () => {
3237
+ const message = "OpenClaw dynamic tool call aborted.";
3238
+ const terminalReason = resolveCodexToolAbortTerminalReason(params.signal);
3239
+ params.onFallbackSelected?.();
3240
+ controller.abort(params.signal.reason ?? /* @__PURE__ */ new Error(message));
3241
+ notifyFailedToolResult(message, terminalReason);
3242
+ resolveAbort?.(createFailedAfterPossibleDispatch(message, terminalReason));
3243
+ };
3244
+ const abortPromise = new Promise((resolve) => {
3245
+ resolveAbort = resolve;
3246
+ });
3247
+ const timeoutPromise = new Promise((resolve) => {
3248
+ const timeoutMs = clampDynamicToolTimeoutMs(params.timeoutMs);
3249
+ timeout = setTimeout(() => {
3250
+ timedOut = true;
3251
+ const timeoutDetails = formatDynamicToolTimeoutDetails({
3252
+ call: params.call,
3253
+ timeoutMs
3254
+ });
3255
+ params.onFallbackSelected?.();
3256
+ controller.abort(new Error(timeoutDetails.responseMessage));
3257
+ params.onTimeout?.();
3258
+ embeddedAgentLog.warn("codex dynamic tool call timed out", {
3259
+ ...timeoutDetails.meta,
3260
+ consoleMessage: timeoutDetails.consoleMessage
3261
+ });
3262
+ notifyFailedToolResult(timeoutDetails.responseMessage, "timed_out");
3263
+ resolve(createFailedAfterPossibleDispatch(timeoutDetails.responseMessage, "timed_out"));
3264
+ }, timeoutMs);
3265
+ timeout.unref?.();
3266
+ });
3267
+ try {
3268
+ params.signal.addEventListener("abort", abortFromRun, { once: true });
3269
+ if (params.signal.aborted) abortFromRun();
3270
+ const response = await Promise.race([
3271
+ params.toolBridge.handleToolCall(params.call, {
3272
+ signal: controller.signal,
3273
+ onAgentToolResult: notifyAgentToolResult,
3274
+ toolCallOrdinal: params.toolCallOrdinal,
3275
+ retainExecutionSnapshot: true
3276
+ }),
3277
+ abortPromise,
3278
+ timeoutPromise
3279
+ ]);
3280
+ if (!response.success && !didNotifyAgentToolResult) notifyFailedToolResult(readDynamicToolResponseText(response), response.diagnosticTerminalReason ?? "failed");
3281
+ return finalizeTerminal(response);
3282
+ } catch (error) {
3283
+ const terminalReason = params.signal.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : resolveToolExecutionErrorKind(error);
3284
+ const message = formatToolExecutionErrorMessage(error, "OpenClaw dynamic tool call failed.");
3285
+ notifyFailedToolResult(message, terminalReason);
3286
+ return finalizeTerminal(createFailedAfterPossibleDispatch(message, terminalReason));
3287
+ } finally {
3288
+ if (timeout) clearTimeout(timeout);
3289
+ params.signal.removeEventListener("abort", abortFromRun);
3290
+ resolveAbort = void 0;
3291
+ if (!timedOut && !controller.signal.aborted) controller.abort(/* @__PURE__ */ new Error("OpenClaw dynamic tool call finished."));
3292
+ }
3293
+ }
3294
+ function readDynamicToolResponseText(response) {
3295
+ return response.contentItems.flatMap((item) => item.type === "inputText" && typeof item.text === "string" ? [item.text] : []).join("\n").trim() || "OpenClaw dynamic tool call failed.";
3296
+ }
3297
+ /** Strips OpenClaw-only metadata before sending a dynamic tool response to Codex. */
3298
+ function toCodexDynamicToolProtocolResponse(response) {
3299
+ return {
3300
+ contentItems: response.contentItems,
3301
+ success: response.success
3302
+ };
3303
+ }
3304
+ /** Adds async-started progress details when a tool result continues out of band. */
3305
+ function toCodexDynamicToolProgressResponse(response, protocolResponse) {
3306
+ const transcriptDetails = isJsonObject(response.transcriptDetails) ? response.transcriptDetails : void 0;
3307
+ const mcpAppPreview = isJsonObject(transcriptDetails?.mcpAppPreview) ? transcriptDetails.mcpAppPreview : void 0;
3308
+ const progressDetails = mcpAppPreview ? { mcpAppPreview } : void 0;
3309
+ if (response.asyncStarted !== true && progressDetails === void 0) return protocolResponse;
3310
+ return {
3311
+ ...protocolResponse,
3312
+ ...progressDetails ? { details: progressDetails } : {},
3313
+ ...response.asyncStarted === true ? { details: {
3314
+ ...progressDetails,
3315
+ async: true,
3316
+ status: "started"
3317
+ } } : {}
3318
+ };
3319
+ }
3320
+ /** Decides whether a terminal dynamic tool response can release the Codex turn. */
3321
+ function shouldReleaseTurnAfterTerminalDynamicTool(state) {
3322
+ return !state.completed && !state.aborted && state.responseSuccess && !state.currentTurnHadNonTerminalDynamicToolResult && state.activeAppServerTurnRequests === 0 && state.activeTurnItemIdsCount === 0 && state.pendingOpenClawDynamicToolCompletionIdsCount === 0;
3323
+ }
3324
+ /** Returns true when a non-async result should block terminal-release shortcuts. */
3325
+ function shouldBlockTerminalReleaseForNonTerminalDynamicToolResult(response) {
3326
+ return response.asyncStarted !== true;
3327
+ }
3328
+ /** Resolves whether terminal diagnostic state should release, wait, or stay idle. */
3329
+ function resolveTerminalDynamicToolBatchAction(state) {
3330
+ if (state.activeAppServerTurnRequests > 0 || state.activeTurnItemIdsCount > 0 || state.pendingOpenClawDynamicToolCompletionIdsCount > 0) return "wait";
3331
+ if (state.currentTurnHadNonTerminalDynamicToolResult) return "clear-nonterminal-batch";
3332
+ if (state.hasPendingTerminalDynamicToolRelease) return "release-pending-terminal";
3333
+ return "idle";
3334
+ }
3335
+ /** Returns true for diagnostic events that terminate a dynamic tool call. */
3336
+ function isDynamicToolTerminalDiagnosticEvent(event) {
3337
+ return event.type === "tool.execution.completed" || event.type === "tool.execution.error" || event.type === "tool.execution.blocked";
3338
+ }
3339
+ /** Matches terminal diagnostics to a specific dynamic tool call id/name. */
3340
+ function isMatchingDynamicToolTerminalDiagnostic(params) {
3341
+ if (params.event.toolCallId !== params.call.callId || params.event.toolName !== params.call.tool) return false;
3342
+ if (params.runId !== void 0) return params.event.runId === params.runId;
3343
+ if (params.sessionId !== void 0) return params.event.sessionId === params.sessionId;
3344
+ if (params.sessionKey !== void 0) return params.event.sessionKey === params.sessionKey;
3345
+ return params.event.runId === void 0 && params.event.sessionId === void 0 && params.event.sessionKey === void 0;
3346
+ }
3347
+ /** Checks pending diagnostics for a terminal event matching a tool call. */
3348
+ function hasPendingDynamicToolTerminalDiagnostic(params) {
3349
+ return hasPendingInternalDiagnosticEvent((event) => {
3350
+ if (!isDynamicToolTerminalDiagnosticEvent(event)) return false;
3351
+ return isMatchingDynamicToolTerminalDiagnostic({
3352
+ event,
3353
+ call: params.call,
3354
+ runId: params.runId,
3355
+ sessionId: params.sessionId,
3356
+ sessionKey: params.sessionKey
3357
+ });
3358
+ });
3359
+ }
3360
+ /** Resolves per-tool timeout, applying media/message defaults and hard caps. */
3361
+ function resolveDynamicToolCallTimeoutMs(params) {
3362
+ if (params.call.tool === "computer") return clampDynamicToolTimeoutMs(readComputerToolTimeoutMs(params.call.arguments));
3363
+ if (params.call.tool === "message") return CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS;
3364
+ if (params.call.tool === "agents_wait") {
3365
+ const requestedMs = readDynamicToolCallTimeoutMs(params.call.arguments) ?? readConfiguredDynamicToolTimeoutMs(params.call.tool, params.config) ?? CODEX_DYNAMIC_AGENTS_WAIT_TOOL_TIMEOUT_MS;
3366
+ return Math.max(1, Math.min(63e4, Math.floor(requestedMs)));
3367
+ }
3368
+ return clampDynamicToolTimeoutMs(readDynamicToolCallTimeoutMs(params.call.arguments) ?? readConfiguredDynamicToolTimeoutMs(params.call.tool, params.config) ?? CODEX_DYNAMIC_TOOL_TIMEOUT_MS);
3369
+ }
3370
+ function readComputerToolTimeoutMs(value) {
3371
+ const args = isJsonObject(value) ? value : void 0;
3372
+ const action = typeof args?.action === "string" ? args.action : void 0;
3373
+ const gatewayTimeoutMs = readPositiveFiniteTimeoutMs(args?.timeoutMs) ?? CODEX_DYNAMIC_COMPUTER_GATEWAY_TIMEOUT_MS;
3374
+ const gatewayCallCount = action === "screenshot" || action === "wait" ? 3 : 4;
3375
+ return (action === "wait" || action === "hold_key" ? Math.max(0, Number(args?.duration) || 0) * 1e3 : 0) + gatewayCallCount * gatewayTimeoutMs + CODEX_DYNAMIC_COMPUTER_COMPLETION_GRACE_MS;
3376
+ }
3377
+ function readDynamicToolCallTimeoutMs(value) {
3378
+ if (!isJsonObject(value)) return;
3379
+ const timeoutMs = readPositiveFiniteTimeoutMs(value.timeoutMs);
3380
+ if (timeoutMs !== void 0) return timeoutMs;
3381
+ const timeoutSecondsMs = readDynamicToolTimeoutSecondsAsMs(value.timeoutSeconds);
3382
+ return timeoutSecondsMs === void 0 ? void 0 : addTimerTimeoutGraceMs(timeoutSecondsMs, CODEX_DYNAMIC_TOOL_TIMEOUT_SECONDS_GRACE_MS);
3383
+ }
3384
+ function readConfiguredDynamicToolTimeoutMs(toolName, config) {
3385
+ if (toolName === "image_generate") {
3386
+ const imageModel = config?.agents?.defaults?.mediaModels?.image;
3387
+ if (!imageModel || typeof imageModel !== "object") return CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
3388
+ return readPositiveFiniteTimeoutMs(imageModel.timeoutMs) ?? CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
3389
+ }
3390
+ if (toolName === "view_image") {
3391
+ const candidates = (config?.tools?.media?.models ?? []).filter((entry) => !entry.capabilities || entry.capabilities.includes("image"));
3392
+ const capabilityTimeoutMs = readTimeoutSecondsAsMs(config?.tools?.media?.image?.timeoutSeconds);
3393
+ return Math.max(capabilityTimeoutMs ?? CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS, ...candidates.map((entry) => readTimeoutSecondsAsMs(entry.timeoutSeconds) ?? capabilityTimeoutMs ?? CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS));
3394
+ }
3395
+ if (toolName === "message") return CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS;
3396
+ }
3397
+ function readTimeoutSecondsAsMs(value) {
3398
+ const seconds = readPositiveFiniteTimeoutMs(value);
3399
+ return seconds === void 0 ? void 0 : seconds * 1e3;
3400
+ }
3401
+ function readDynamicToolTimeoutSecondsAsMs(value) {
3402
+ if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) return;
3403
+ return value * 1e3;
3404
+ }
3405
+ function readPositiveFiniteTimeoutMs(value) {
3406
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
3407
+ }
3408
+ function clampDynamicToolTimeoutMs(timeoutMs) {
3409
+ return Math.max(1, Math.min(CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS, Math.floor(timeoutMs)));
3410
+ }
3411
+ //#endregion
3412
+ //#region extensions/codex/src/app-server/native-hook-relay-state.ts
3413
+ const pending = /* @__PURE__ */ new Set();
3414
+ /** Owns delayed hook-relay cleanup across runtime scheduling and test teardown. */
3415
+ const nativeHookRelayUnregisterQueue = {
3416
+ add(entry) {
3417
+ pending.add(entry);
3418
+ },
3419
+ delete(entry) {
3420
+ return pending.delete(entry);
3421
+ },
3422
+ flush() {
3423
+ while (pending.size > 0) {
3424
+ const entry = pending.values().next().value;
3425
+ if (!entry) return;
3426
+ clearTimeout(entry.timeout);
3427
+ entry.unregister();
3428
+ }
3429
+ },
3430
+ clear() {
3431
+ for (const entry of pending) clearTimeout(entry.timeout);
3432
+ pending.clear();
3433
+ }
3434
+ };
3435
+ //#endregion
3436
+ //#region extensions/codex/src/app-server/native-hook-relay.ts
3437
+ /**
3438
+ * Bridges Codex native hook callbacks into OpenClaw's native hook relay so
3439
+ * app-server tool events can still run OpenClaw policy and diagnostics.
3440
+ */
3441
+ /** Codex hook events that can be registered through OpenClaw's native relay. */
3442
+ const CODEX_NATIVE_HOOK_RELAY_EVENTS = [
3443
+ "pre_tool_use",
3444
+ "post_tool_use",
3445
+ "permission_request",
3446
+ "before_agent_finalize"
3447
+ ];
3448
+ const CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS = CODEX_NATIVE_HOOK_RELAY_EVENTS.filter((event) => event !== "permission_request");
3449
+ const CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS = 30 * 6e4;
3450
+ /** Extra relay lifetime after the expected turn budget, preventing late hook drops. */
3451
+ const CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS = 5 * 6e4;
3452
+ const CODEX_NATIVE_HOOK_RELAY_COMMAND_MIN_PARENT_MARGIN_MS = 250;
3453
+ const CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS = 1e3;
3454
+ const CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC = 10;
3455
+ const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS = 1e4;
3456
+ const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS = 5e3;
3457
+ const MAX_PENDING_DIRECT_CHILD_ADMISSIONS = 32;
3458
+ const nativeHookPolicyByClient = /* @__PURE__ */ new WeakMap();
3459
+ const CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID = {
3460
+ exec: [
3461
+ "Bash",
3462
+ "exec",
3463
+ "exec_command"
3464
+ ],
3465
+ apply_patch: [
3466
+ "apply_patch",
3467
+ "Write",
3468
+ "Edit"
3469
+ ],
3470
+ spawn_agent: ["spawn_agent", "Agent"]
3471
+ };
3472
+ /** Enterprise managed-only policy silently drops the session-layer hooks that enforce OpenClaw. */
3473
+ async function assertCodexNativeHookRelayAllowed(client, signal) {
3474
+ let attestation = nativeHookPolicyByClient.get(client);
3475
+ if (!attestation) {
3476
+ attestation = client.request("configRequirements/read", void 0, { signal }).then((response) => {
3477
+ if (!isJsonObject(response) || !Object.hasOwn(response, "requirements")) throw new Error("Codex configRequirements/read returned an invalid hook policy response");
3478
+ const requirements = response.requirements;
3479
+ if (requirements === null) return;
3480
+ if (!isJsonObject(requirements)) throw new Error("Codex configRequirements/read returned invalid hook policy requirements");
3481
+ const managedOnly = requirements.allowManagedHooksOnly;
3482
+ if (managedOnly !== void 0 && managedOnly !== null && typeof managedOnly !== "boolean") throw new Error("Codex configRequirements/read returned invalid managed-only hook policy");
3483
+ if (managedOnly === true) throw new Error("Codex managed-only hooks disable the OpenClaw native hook relay; refusing unenforced execution");
3484
+ });
3485
+ nativeHookPolicyByClient.set(client, attestation);
3486
+ attestation.catch(() => {
3487
+ if (nativeHookPolicyByClient.get(client) === attestation) nativeHookPolicyByClient.delete(client);
3488
+ });
3489
+ }
3490
+ await attestation;
3491
+ }
3492
+ /** Defers relay unregister so late native hook subprocesses can still resolve. */
3493
+ function scheduleCodexNativeHookRelayUnregister(params) {
3494
+ let pending;
3495
+ const unregister = () => {
3496
+ if (!pending) return;
3497
+ const current = pending;
3498
+ pending = void 0;
3499
+ if (!nativeHookRelayUnregisterQueue.delete(current)) return;
3500
+ params.relay.unregister();
3501
+ };
3502
+ const timeout = setTimeout(unregister, resolveCodexNativeHookRelayUnregisterGraceMs(params.hookTimeoutSec));
3503
+ pending = {
3504
+ timeout,
3505
+ unregister
3506
+ };
3507
+ nativeHookRelayUnregisterQueue.add(pending);
3508
+ timeout.unref();
3509
+ }
3510
+ /** Computes the delayed unregister window from Codex's hook timeout. */
3511
+ function resolveCodexNativeHookRelayUnregisterGraceMs(hookTimeoutSec) {
3512
+ const hookTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 0;
3513
+ return Math.max(CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS, addTimerTimeoutGraceMs(hookTimeoutMs, CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS) ?? 0);
3514
+ }
3515
+ /** Records a native pre-tool failure that Codex does not project as a tool item. */
3516
+ function emitCodexNativePreToolUseFailureDiagnostic(params) {
3517
+ emitTrustedDiagnosticEvent({
3518
+ type: "tool.execution.error",
3519
+ ...params.agentId ? { agentId: params.agentId } : {},
3520
+ sessionId: params.sessionId,
3521
+ ...params.sessionKey ? { sessionKey: params.sessionKey } : {},
3522
+ runId: params.runId,
3523
+ toolName: params.failure.toolName,
3524
+ toolCallId: params.failure.toolCallId,
3525
+ durationMs: params.failure.durationMs,
3526
+ errorCategory: "before_tool_call",
3527
+ terminalReason: params.terminalReason ?? (params.signal?.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : params.failure.disposition),
3528
+ ...params.sourceTimestampMs !== void 0 ? { sourceTimestampMs: params.sourceTimestampMs } : {}
3529
+ });
3530
+ }
3531
+ /** Registers an OpenClaw native hook relay for a Codex app-server turn. */
3532
+ function createCodexNativeHookRelay(params) {
3533
+ if (params.options?.enabled === false) return;
3534
+ const directChildClaims = /* @__PURE__ */ new Map();
3535
+ const pendingDirectChildAdmissions = /* @__PURE__ */ new Map();
3536
+ let foregroundClosed = false;
3537
+ let successfulYieldRetentionAuthorized = false;
3538
+ const assertClaim = (threadId, claim) => () => directChildClaims.get(threadId) === claim;
3539
+ const rejectPendingAdmissions = (reason) => {
3540
+ for (const pending of pendingDirectChildAdmissions.values()) pending.reject(new Error(reason));
3541
+ pendingDirectChildAdmissions.clear();
3542
+ };
3543
+ const relay = registerRetainedNativeHookRelayForBundledRuntime({
3544
+ provider: "codex",
3545
+ relayId: buildCodexNativeHookRelayId({
3546
+ agentId: params.agentId,
3547
+ sessionId: params.sessionId,
3548
+ sessionKey: params.sessionKey
3549
+ }),
3550
+ ...params.generation ? { generation: params.generation } : {},
3551
+ ...params.generationMismatchGraceMs ? { generationMismatchGraceMs: params.generationMismatchGraceMs } : {},
3552
+ ...params.agentId ? { agentId: params.agentId } : {},
3553
+ sessionId: params.sessionId,
3554
+ ...params.sessionKey ? { sessionKey: params.sessionKey } : {},
3555
+ ...params.config ? { config: params.config } : {},
3556
+ runId: params.runId,
3557
+ ...params.channelId ? { channelId: params.channelId } : {},
3558
+ ...params.requester ? { requester: params.requester } : {},
3559
+ ...params.approvalContext ? { approvalContext: params.approvalContext } : {},
3560
+ allowedEvents: params.events,
3561
+ preToolUseLoopDetection: params.loopDetectionPreToolUseRelay,
3562
+ ttlMs: resolveCodexNativeHookRelayTtlMs({
3563
+ explicitTtlMs: params.options?.ttlMs,
3564
+ attemptTimeoutMs: params.attemptTimeoutMs,
3565
+ startupTimeoutMs: params.startupTimeoutMs,
3566
+ turnStartTimeoutMs: params.turnStartTimeoutMs
3567
+ }),
3568
+ signal: params.signal,
3569
+ runBeforeToolCall: params.hostCapabilities.runBeforeToolCall,
3570
+ assertActive: params.hostCapabilities.assertActive,
3571
+ retention: {
3572
+ readClaim: readCodexNativeChildThreadId,
3573
+ shouldRetainAfterForegroundClose: () => successfulYieldRetentionAuthorized && directChildClaims.size > 0,
3574
+ allowPreToolUse: (childThreadId) => directChildClaims.has(childThreadId),
3575
+ awaitForegroundAdmission: (childThreadId) => {
3576
+ if (foregroundClosed) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission unavailable"));
3577
+ const existingClaim = directChildClaims.get(childThreadId);
3578
+ if (existingClaim) return Promise.resolve(assertClaim(childThreadId, existingClaim));
3579
+ const existingPending = pendingDirectChildAdmissions.get(childThreadId);
3580
+ if (existingPending) return existingPending.promise.then((claim) => assertClaim(childThreadId, claim));
3581
+ if (pendingDirectChildAdmissions.size >= MAX_PENDING_DIRECT_CHILD_ADMISSIONS) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission capacity reached"));
3582
+ const { promise, resolve, reject } = createDeferred();
3583
+ pendingDirectChildAdmissions.set(childThreadId, {
3584
+ promise,
3585
+ resolve,
3586
+ reject
3587
+ });
3588
+ return promise.then((claim) => assertClaim(childThreadId, claim));
3589
+ },
3590
+ onDispose: () => {
3591
+ foregroundClosed = true;
3592
+ rejectPendingAdmissions("native hook relay registration closed");
3593
+ }
3594
+ },
3595
+ onPreToolUseFailure: params.onPreToolUseFailure,
3596
+ command: {
3597
+ nice: 10,
3598
+ timeoutMs: params.options?.gatewayTimeoutMs
3599
+ }
3600
+ });
3601
+ const unregister = () => {
3602
+ foregroundClosed = true;
3603
+ rejectPendingAdmissions("native hook relay foreground closed");
3604
+ relay.unregister();
3605
+ };
3606
+ return {
3607
+ ...relay,
3608
+ unregister,
3609
+ authorizeRetentionAfterSuccessfulYield: () => {
3610
+ successfulYieldRetentionAuthorized = true;
3611
+ },
3612
+ hasClaimedDirectChild: () => directChildClaims.size > 0,
3613
+ rejectPendingDirectChild: (threadIdInput, reason) => {
3614
+ const threadId = threadIdInput.trim();
3615
+ const pending = threadId ? pendingDirectChildAdmissions.get(threadId) : void 0;
3616
+ if (!pending) return;
3617
+ pendingDirectChildAdmissions.delete(threadId);
3618
+ pending.reject(new Error(reason));
3619
+ },
3620
+ claimDirectChild: (threadIdInput) => {
3621
+ const threadId = threadIdInput.trim();
3622
+ if (!threadId) return () => void 0;
3623
+ if (directChildClaims.get(threadId)) return () => void 0;
3624
+ const claim = Symbol(threadId);
3625
+ directChildClaims.set(threadId, claim);
3626
+ const pending = pendingDirectChildAdmissions.get(threadId);
3627
+ pendingDirectChildAdmissions.delete(threadId);
3628
+ pending?.resolve(claim);
3629
+ let released = false;
3630
+ return () => {
3631
+ if (released) return;
3632
+ released = true;
3633
+ if (directChildClaims.get(threadId) !== claim) return;
3634
+ directChildClaims.delete(threadId);
3635
+ if (foregroundClosed && directChildClaims.size === 0) relay.unregister();
3636
+ };
3637
+ }
3638
+ };
3639
+ }
3640
+ function readCodexNativeChildThreadId(rawPayload) {
3641
+ if (!isJsonObject(rawPayload) || typeof rawPayload.agent_id !== "string") return;
3642
+ return rawPayload.agent_id.trim() || void 0;
3643
+ }
3644
+ /** Selects the native hook events Codex should install for the current approval mode. */
3645
+ function resolveCodexNativeHookRelayEvents(params) {
3646
+ if (params.configuredEvents?.length) return params.configuredEvents;
3647
+ return params.appServer.approvalPolicy === "never" ? CODEX_NATIVE_HOOK_RELAY_EVENTS : CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS;
3648
+ }
3649
+ /** Derives the native hook relay TTL from the turn budget unless explicitly configured. */
3650
+ function resolveCodexNativeHookRelayTtlMs(params) {
3651
+ if (params.explicitTtlMs !== void 0) return params.explicitTtlMs;
3652
+ const relayBudgetMs = params.attemptTimeoutMs + params.startupTimeoutMs + params.turnStartTimeoutMs + CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS;
3653
+ return Math.max(CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS, Math.floor(relayBudgetMs));
3654
+ }
3655
+ /** Builds a stable relay id scoped to the agent and session identity. */
3656
+ function buildCodexNativeHookRelayId(params) {
3657
+ const hash = createHash("sha256");
3658
+ hash.update("openclaw:codex:native-hook-relay:v1");
3659
+ hash.update("\0");
3660
+ hash.update(params.agentId?.trim() || "");
3661
+ hash.update("\0");
3662
+ hash.update(params.sessionKey?.trim() || params.sessionId);
3663
+ return `codex-${hash.digest("hex").slice(0, 40)}`;
3664
+ }
3665
+ const CODEX_HOOK_EVENT_BY_NATIVE_EVENT = {
3666
+ pre_tool_use: "PreToolUse",
3667
+ post_tool_use: "PostToolUse",
3668
+ permission_request: "PermissionRequest",
3669
+ before_agent_finalize: "Stop"
3670
+ };
3671
+ const CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT = {
3672
+ pre_tool_use: "pre_tool_use",
3673
+ post_tool_use: "post_tool_use",
3674
+ permission_request: "permission_request",
3675
+ before_agent_finalize: "stop"
3676
+ };
3677
+ const CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS = ["/<session-flags>/config.toml", "<session-flags>/config.toml"];
3678
+ /** Builds the Codex config overlay that installs trusted command hooks for relay events. */
3679
+ function buildCodexNativeHookRelayConfig(params) {
3680
+ const events = params.events?.length ? params.events : CODEX_NATIVE_HOOK_RELAY_EVENTS;
3681
+ const selectedEvents = new Set(events);
3682
+ const config = { "features.hooks": true };
3683
+ const hookState = {};
3684
+ for (const event of CODEX_NATIVE_HOOK_RELAY_EVENTS) {
3685
+ const codexEvent = CODEX_HOOK_EVENT_BY_NATIVE_EVENT[event];
3686
+ const selected = selectedEvents.has(event);
3687
+ const shouldRelay = params.relay.shouldRelayEvent(event);
3688
+ if (!selected || !shouldRelay) {
3689
+ if (selected || params.clearOmittedEvents) config[`hooks.${codexEvent}`] = [];
3690
+ 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 };
3691
+ continue;
3692
+ }
3693
+ const timeout = normalizeHookTimeoutSec(params.hookTimeoutSec);
3694
+ const command = params.relay.commandForEvent(event, { timeoutMs: resolveCodexNativeHookRelayCommandTimeoutMs(timeout) });
3695
+ const matcher = buildCodexNativeToolMatcher(params.relay.toolMatcherForEvent(event));
3696
+ config[`hooks.${codexEvent}`] = [{
3697
+ ...matcher ? { matcher } : {},
3698
+ hooks: [{
3699
+ type: "command",
3700
+ command,
3701
+ timeout,
3702
+ async: false,
3703
+ statusMessage: "OpenClaw native hook relay"
3704
+ }]
3705
+ }];
3706
+ const state = {
3707
+ enabled: true,
3708
+ trusted_hash: codexCommandHookTrustedHash({
3709
+ event,
3710
+ command,
3711
+ matcher,
3712
+ timeout,
3713
+ statusMessage: "OpenClaw native hook relay"
3714
+ })
3715
+ };
3716
+ for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = state;
3717
+ }
3718
+ config["hooks.state"] = hookState;
3719
+ return config;
3720
+ }
3721
+ /** Builds a Codex config overlay that disables native hooks and clears hook arrays. */
3722
+ function buildCodexNativeHookRelayDisabledConfig() {
3723
+ return {
3724
+ "features.hooks": false,
3725
+ "hooks.PreToolUse": [],
3726
+ "hooks.PostToolUse": [],
3727
+ "hooks.PermissionRequest": [],
3728
+ "hooks.Stop": []
3729
+ };
3730
+ }
3731
+ function normalizeHookTimeoutSec(value) {
3732
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.ceil(value) : CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC;
3733
+ }
3734
+ function resolveCodexNativeHookRelayCommandTimeoutMs(hookTimeoutSec) {
3735
+ const parentTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 5e3;
3736
+ 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)));
3737
+ return Math.max(1, parentTimeoutMs - parentMarginMs);
3738
+ }
3739
+ function buildCodexNativeToolMatcher(toolNames) {
3740
+ if (toolNames === void 0) return;
3741
+ if (toolNames.length === 0) throw new TypeError("Codex native hook matcher requires at least one tool name");
3742
+ const nativeNames = /* @__PURE__ */ new Set();
3743
+ let hasCustomToolName = false;
3744
+ for (const toolName of toolNames) {
3745
+ const canonicalToolName = toolName.trim();
3746
+ if (!canonicalToolName || canonicalToolName === "*") throw new TypeError("Codex native hook matcher requires canonical OpenClaw tool ids");
3747
+ const nativeAliases = CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID[canonicalToolName];
3748
+ if (!nativeAliases) hasCustomToolName = true;
3749
+ for (const nativeName of nativeAliases ?? [canonicalToolName]) nativeNames.add(nativeName);
3750
+ }
3751
+ const sortedNames = Array.from(nativeNames).toSorted();
3752
+ if (!hasCustomToolName && sortedNames.every((toolName) => /^[A-Za-z0-9_]+$/.test(toolName))) return sortedNames.join("|");
3753
+ return `(?i)^(?:${sortedNames.map((toolName) => toolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})$`;
3754
+ }
3755
+ function codexCommandHookTrustedHash(params) {
3756
+ const identity = {
3757
+ event_name: CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[params.event],
3758
+ ...params.matcher ? { matcher: params.matcher } : {},
3759
+ hooks: [{
3760
+ async: false,
3761
+ command: params.command,
3762
+ statusMessage: params.statusMessage,
3763
+ timeout: params.timeout,
3764
+ type: "command"
3765
+ }]
3766
+ };
3767
+ return `sha256:${createHash("sha256").update(JSON.stringify(sortJsonValue(identity))).digest("hex")}`;
3768
+ }
3769
+ function sortJsonValue(value) {
3770
+ if (!value || typeof value !== "object") return value;
3771
+ if (Array.isArray(value)) return value.map(sortJsonValue);
3772
+ const sorted = {};
3773
+ for (const [key, entry] of Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right))) sorted[key] = sortJsonValue(entry);
3774
+ return sorted;
3775
+ }
2757
3776
  //#endregion
2758
3777
  //#region extensions/codex/src/app-server/profiler-flag.ts
2759
3778
  const PROFILER_FLAGS = ["profiler", "codex.profiler"];
@@ -2853,6 +3872,13 @@ function createCodexThreadLifecycleTimingTracker(options = {}) {
2853
3872
  //#endregion
2854
3873
  //#region extensions/codex/src/app-server/thread-lifecycle-preflight.ts
2855
3874
  async function prepareCodexThreadLifecyclePreflight(params) {
3875
+ await assertCodexModelBackedReviewerEffectiveConfig({
3876
+ client: params.client,
3877
+ approvalsReviewer: params.appServer.approvalsReviewer,
3878
+ cwd: params.cwd,
3879
+ signal: params.signal
3880
+ });
3881
+ if (params.nativeHookRelayRequired) await assertCodexNativeHookRelayAllowed(params.client, params.signal);
2856
3882
  const lifecycleTiming = createCodexThreadLifecycleTimingTracker({
2857
3883
  ...params.timing,
2858
3884
  enabled: params.timing?.enabled ?? isCodexAppServerProfilerEnabled(params.params.config)
@@ -2898,9 +3924,13 @@ async function prepareCodexThreadLifecyclePreflight(params) {
2898
3924
  const ringZeroActive = hostSystemAgentActive && isSystemAgentOnlyCodexDynamicToolAllowlist(params.params.toolsAllow);
2899
3925
  const messageOnlySourceReply = isMessageOnlyCodexSourceReply(params.params);
2900
3926
  const restrictedToolSurface = ringZeroActive || messageOnlySourceReply || params.params.pluginHarnessToolPolicyRestricted === true;
3927
+ const imageGenerationDenied = params.params.pluginHarnessToolPolicySafeDeniedTools?.includes("image_generate") === true;
2901
3928
  if (restrictedToolSurface && params.nativeCodeModeEnabled !== false) throw new Error("Codex restricted tool surfaces require native code mode to be disabled");
2902
3929
  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));
3930
+ if (restrictedToolSurface || imageGenerationDenied) await lifecycleTiming.measure("tool-policy-config-requirements-read", () => assertCodexManagedRequirementsDoNotOverrideToolPolicy(params.client, {
3931
+ restrictedToolSurface,
3932
+ additionalDeniedFeatures: imageGenerationDenied ? ["image_generation"] : void 0
3933
+ }, params.signal));
2904
3934
  const ringZeroConfigFingerprint = ringZeroActive ? fingerprintJsonObject({
2905
3935
  version: 1,
2906
3936
  baseInstructions: "",
@@ -2968,6 +3998,7 @@ async function tryReuseCodexLiveThread(options) {
2968
3998
  const resumeConfig = mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, prebuiltFinalConfigPatch.configPatch);
2969
3999
  const resumeParams = lifecycleTiming.measureSync("warm-thread-resume-params", () => buildThreadResumeParams(params.params, {
2970
4000
  threadId: binding.threadId,
4001
+ cwd: params.cwd,
2971
4002
  authProfileId: resumeAuthProfileId,
2972
4003
  model: startModelSelection.model,
2973
4004
  modelProvider: startModelProvider,
@@ -2981,7 +4012,9 @@ async function tryReuseCodexLiveThread(options) {
2981
4012
  nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
2982
4013
  webSearchAllowed: params.webSearchAllowed,
2983
4014
  hostSystemAgentActive,
2984
- restrictedToolSurfaceInheritedMcpServerNames
4015
+ restrictedToolSurfaceInheritedMcpServerNames,
4016
+ shellEnvironment: params.shellEnvironment,
4017
+ disableLoginShell: params.disableLoginShell
2985
4018
  }));
2986
4019
  const liveThreadConfigFingerprint = fingerprintCodexThreadConfig({
2987
4020
  ...resumeParams,
@@ -3102,7 +4135,9 @@ async function materializePendingSupervisionBranch(params) {
3102
4135
  model: nativeModel,
3103
4136
  modelProvider: nativeModelProvider,
3104
4137
  hostSystemAgentActive: params.hostSystemAgentActive,
3105
- restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames
4138
+ restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames,
4139
+ shellEnvironment: params.shellEnvironment,
4140
+ disableLoginShell: params.disableLoginShell
3106
4141
  });
3107
4142
  assertExactSupervisionModelSelection(startParams, {
3108
4143
  model: nativeModel,
@@ -3258,7 +4293,9 @@ function buildPendingSupervisionProbeForkParams(params, pending) {
3258
4293
  webSearchAllowed: params.webSearchAllowed,
3259
4294
  appServer: params.appServer,
3260
4295
  hostSystemAgentActive: params.hostSystemAgentActive,
3261
- restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames
4296
+ restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames,
4297
+ shellEnvironment: params.shellEnvironment,
4298
+ disableLoginShell: params.disableLoginShell
3262
4299
  });
3263
4300
  return {
3264
4301
  threadId: pending.sourceThreadId,
@@ -3465,6 +4502,8 @@ async function startOrResumeThread(params) {
3465
4502
  hostSystemAgentActive,
3466
4503
  restrictedToolSurface,
3467
4504
  restrictedToolSurfaceInheritedMcpServerNames,
4505
+ shellEnvironment: params.shellEnvironment,
4506
+ disableLoginShell: params.disableLoginShell,
3468
4507
  environmentSelection: params.environmentSelection,
3469
4508
  provisionalAppIds: pluginThreadConfig?.provisionalAppIds,
3470
4509
  signal: params.signal,
@@ -3475,6 +4514,7 @@ async function startOrResumeThread(params) {
3475
4514
  cwd: params.cwd,
3476
4515
  ...clientId ? { clientId } : {},
3477
4516
  authProfileId: void 0,
4517
+ agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
3478
4518
  preserveNativeModel: true,
3479
4519
  dynamicToolsFingerprint,
3480
4520
  dynamicToolsContainDeferred,
@@ -3541,7 +4581,7 @@ async function startOrResumeThread(params) {
3541
4581
  }
3542
4582
  const startModelSelection = resolveCodexAppServerThreadModelSelection({
3543
4583
  provider: params.params.provider,
3544
- model: params.params.modelId,
4584
+ model: params.runtimeModelId ?? params.params.modelId,
3545
4585
  binding,
3546
4586
  authProfileId: params.params.authProfileId,
3547
4587
  authProfileStore: params.params.authProfileStore,
@@ -3855,9 +4895,10 @@ function buildTurnStartParams(params, options) {
3855
4895
  input: [...buildCodexUserInput(options.promptText ?? params.prompt, params.images), ...options.explicitSkillInputs ?? []],
3856
4896
  ...additionalContext ? { additionalContext } : {},
3857
4897
  cwd: options.cwd,
4898
+ ...options.appServer.sessionRoot ? { runtimeWorkspaceRoots: [options.appServer.sessionRoot] } : {},
3858
4899
  approvalPolicy: options.appServer.approvalPolicy,
3859
4900
  approvalsReviewer: options.appServer.approvalsReviewer,
3860
- ...useThreadPermissionProfile ? {} : { sandboxPolicy: options.sandboxPolicy ?? codexSandboxPolicyForTurn(options.appServer.sandbox, options.cwd, options.appServer.start?.args) },
4901
+ ...useThreadPermissionProfile ? {} : { sandboxPolicy: options.sandboxPolicy ?? codexSandboxPolicyForTurn(options.appServer.sandbox, options.appServer.sessionRoot ?? options.cwd, options.appServer.start?.args) },
3861
4902
  ...modelSelection ? {
3862
4903
  model: modelSelection.model,
3863
4904
  personality: CODEX_NATIVE_PERSONALITY_NONE
@@ -3887,7 +4928,6 @@ function buildTurnCollaborationMode(params, options = {}) {
3887
4928
  function buildTurnScopedCollaborationInstructions(params, options = {}) {
3888
4929
  const contextInstructions = joinPresentSections(options.turnScopedDeveloperInstructions, options.memoryCollaborationInstructions, options.skillsCollaborationInstructions);
3889
4930
  if (params.trigger === "cron") return joinPresentSections(buildCronCollaborationInstructions(), contextInstructions);
3890
- if (params.trigger === "heartbeat") return joinPresentSections(buildHeartbeatCollaborationInstructions(), contextInstructions);
3891
4931
  if (contextInstructions?.trim()) return joinPresentSections(buildDefaultCollaborationInstructions(), contextInstructions);
3892
4932
  return null;
3893
4933
  }
@@ -3914,15 +4954,8 @@ function buildCronCollaborationInstructions() {
3914
4954
  "Keep output concise and automation-oriented. Prefer the final command result or a short failure summary over status narration."
3915
4955
  ].join("\n\n");
3916
4956
  }
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
4957
  function joinPresentSections(...sections) {
3925
4958
  return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
3926
4959
  }
3927
4960
  //#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 };
4961
+ export { isForcedPrivateQaCodexRuntime as $, attestCodexRestrictedToolSurfaceMcpServersDisabled as A, resolveCodexAppServerRequestModelSelection as B, toCodexDynamicToolProgressResponse as C, refreshCodexPluginRuntimeState as Ct, withDynamicToolExecutionState as D, resolveRecoverableCodexPluginConfigKeys as Dt, createFailedDynamicToolResponse as E, pluginReadParams as Et, resolveCodexWebSearchPlan as F, CODEX_MANAGED_THREAD_MAX_ENTRIES as G, resolveCodexBindingModelProviderFallback as H, buildDeveloperInstructions as I, areCodexDynamicToolFingerprintsCompatible as J, CODEX_MANAGED_THREAD_NAMESPACE as K, buildCodexProjectDocThreadConfig as L, buildCodexRuntimeThreadConfig as M, readCodexInheritedMcpServerNames as N, withDynamicToolTranscriptDetails as O, buildCodexNativeWebSearchThreadConfig as P, filterCodexDynamicToolsForDisabledNativeSurface as Q, CODEX_NATIVE_PERSONALITY_NONE as R, shouldReleaseTurnAfterTerminalDynamicTool as S, ensureCodexPluginActivation as St, resolveCodexToolAbortTerminalReason as T, isOpenAiCuratedMarketplaceName as Tt, resolveReasoningEffort as U, resolveCodexAppServerThreadModelSelection as V, readCodexSupportedReasoningEfforts as W, codexLegacyDynamicToolsFingerprint as X, codexDynamicToolsFingerprint as Y, filterCodexDynamicTools as Z, isDynamicToolTerminalDiagnosticEvent as _, buildCodexPluginThreadConfigTimeoutFallback as _t, isCodexAppServerProfilerEnabled as a, buildContextEngineBinding as at, resolveTerminalDynamicToolBatchAction as b, mergeCodexThreadConfigs as bt, buildCodexNativeHookRelayConfig as c, fitCodexProjectedContextForTurnStart as ct, emitCodexNativePreToolUseFailureDiagnostic as d, resolveCodexContextEngineProjectionMaxChars as dt, isMessageOnlyCodexSourceReply as et, resolveCodexNativeHookRelayEvents as f, resolveCodexContextEngineProjectionReserveTokens as ft, hasPendingDynamicToolTerminalDiagnostic as g, buildCodexPluginThreadConfigInputFingerprint as gt, handleDynamicToolCallWithTimeout as h, buildCodexPluginThreadConfig as ht, startOrResumeThread as i, resolveCodexDynamicToolsLoadingForRuntime as it, buildCodexRingZeroThreadConfigPatch as j, assertCodexManagedRequirementsDoNotOverrideToolPolicy as k, buildCodexNativeHookRelayDisabledConfig as l, neutralizeCodexExplicitMentionSigils as lt, scheduleCodexNativeHookRelayUnregister as m, buildCodexPluginAppsConfigPatchFromPolicyContext as mt, buildTurnStartParams as n, normalizeCodexDynamicToolName as nt, CODEX_NATIVE_HOOK_RELAY_EVENTS as o, isContextEngineBindingCompatible as ot, resolveCodexNativeHookRelayTtlMs as p, resolveCodexContinuityProjectionMaxChars as pt, createCodexManagedThreadStore as q, buildCodexUserInput as r, resolveCodexDynamicToolsLoading as rt, CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS as s, buildCodexContinuityCalibration as st, buildTurnCollaborationMode as t, isSystemAgentOnlyCodexDynamicToolAllowlist as tt, createCodexNativeHookRelay as u, projectContextEngineAssemblyForCodex as ut, isMatchingDynamicToolTerminalDiagnostic as v, buildDisabledAppsConfigPatch as vt, toCodexDynamicToolProtocolResponse as w, isOpenAiCuratedMarketplace as wt, shouldBlockTerminalReleaseForNonTerminalDynamicToolResult as x, shouldBuildCodexPluginThreadConfig as xt, resolveDynamicToolCallTimeoutMs as y, buildPluginAppPolicyContext as yt, resolveCodexAppServerModelProvider as z };