@openclaw/codex 2026.7.2-beta.4 → 2026.7.2-beta.7

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 (40) hide show
  1. package/dist/{app-server-policy-BO3jYBkv.js → app-server-policy-BSaO2JIT.js} +1 -1
  2. package/dist/{attempt-notifications-DdmnBMUp.js → attempt-notifications-AP-a-Q5k.js} +1 -1
  3. package/dist/{bounded-turn-CaQL96kg.js → bounded-turn-D2ZAgZVz.js} +54 -24
  4. package/dist/{capabilities-CLICvTxf.js → capabilities-BFDTVYr4.js} +1 -1
  5. package/dist/cli-metadata.js +1 -1
  6. package/dist/{command-formatters-DgADqnHa.js → command-formatters-C336eRVS.js} +2 -2
  7. package/dist/{command-handlers-l_lVPNlB.js → command-handlers-DAHUSry9.js} +1233 -1222
  8. package/dist/{compact-CC5KhBzX.js → compact-D_uvHurS.js} +8 -8
  9. package/dist/{computer-use-Ip1-QGa-.js → computer-use-CYEc8U3w.js} +123 -115
  10. package/dist/{conversation-control-sNPsbTCP.js → conversation-control-xsIeXbo4.js} +6 -6
  11. package/dist/doctor-contract-api.js +24 -86
  12. package/dist/{dynamic-tools-CQI3UwqY.js → dynamic-tools-D2gYkSBO.js} +482 -91
  13. package/dist/effective-mcp-catalog-Bbd98Inw.js +114 -0
  14. package/dist/{event-projector-usage-D6tE7_qD.js → event-projector-usage-Xp4AlR2k.js} +20 -3
  15. package/dist/harness.js +19 -14
  16. package/dist/index.js +292 -328
  17. package/dist/media-understanding-provider.js +6 -1
  18. package/dist/{models-BNeMlHgg.js → models-BBMKU4jA.js} +2 -2
  19. package/dist/{notification-correlation-BGLixv5_.js → notification-correlation-CU6_AnOb.js} +3 -3
  20. package/dist/{plugin-app-cache-key-CvzvmBRz.js → plugin-app-cache-key-DztPafOA.js} +135 -42
  21. package/dist/{rate-limits-D0IJT-a6.js → rate-limits-CU-gVCCS.js} +1 -1
  22. package/dist/{request-BTccUTD3.js → request-qKh331yu.js} +3 -3
  23. package/dist/{run-attempt--858NXDl.js → run-attempt-CY9WlDLc.js} +1060 -402
  24. package/dist/{runtime-artifact-C2ITXdhM.js → runtime-artifact-CmtG_t1L.js} +90 -24
  25. package/dist/{sandbox-guard-D1Wky__S.js → sandbox-guard-NuhpXmPB.js} +4 -0
  26. package/dist/{session-binding-ZSMX1omP.js → session-binding-BXpo9ymA.js} +1006 -891
  27. package/dist/{session-catalog-DsKaezgR.js → session-catalog-DyOgR4WH.js} +289 -56
  28. package/dist/{session-cli-CYJTfRLG.js → session-cli-2PZaFLRh.js} +2 -2
  29. package/dist/{session-upstream-marker-D7XlI9us.js → session-upstream-marker-B6U7tLw5.js} +1 -1
  30. package/dist/{settled-turn-finalizer-DHltNgtF.js → settled-turn-finalizer-CXY2Z9dT.js} +19 -6
  31. package/dist/{shared-client-wmgNFNYp.js → shared-client-DTbvH-XV.js} +1993 -1720
  32. package/dist/{side-question-BfFZCsJ-.js → side-question-_ulBk1AL.js} +12 -10
  33. package/dist/{thread-lifecycle-cqrOlJkq.js → thread-lifecycle-DF53ogiA.js} +980 -346
  34. package/dist/{transcript-mirror-2p7kl6e4.js → transcript-mirror-DzsdNCf5.js} +80 -41
  35. package/dist/{upstream-session-fork-vPjwkT1i.js → upstream-session-fork-Dy4Xvuoj.js} +3 -3
  36. package/dist/{usage-DM_I3mPT.js → usage-CQ6GhD5-.js} +3 -3
  37. package/dist/{web-search-provider.runtime-Bx6HAc_k.js → web-search-provider.runtime-CCFhg2z5.js} +3 -3
  38. package/package.json +6 -8
  39. package/npm-shrinkwrap.json +0 -202
  40. package/dist/{auth-binding-C1czuHPA.js → auth-binding-B4FfbcCg.js} +1 -1
@@ -1,20 +1,23 @@
1
- import { Q as isJsonObject, X as CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE, c as releaseLeasedSharedCodexAppServerClient, j as resolveCodexGatewayTimeoutWithGraceMs } from "./shared-client-wmgNFNYp.js";
2
- import { b as readCodexPluginConfig } from "./session-binding-ZSMX1omP.js";
3
- import { D as isForcedPrivateQaCodexRuntime, E as filterCodexDynamicToolsWithOpenClawShell, O as isSystemAgentOnlyCodexDynamicToolAllowlist, T as filterCodexDynamicTools, ct as readItemString, h as resolveCodexWebSearchPlan, i as invalidInlineImageText, k as normalizeCodexDynamicToolName, mt as readString$2, o as sanitizeInlineImageDataUrl, st as readItem } from "./thread-lifecycle-cqrOlJkq.js";
4
- import { S as readCodexTurn } from "./transcript-mirror-2p7kl6e4.js";
5
- import { r as formatCodexDisplayText } from "./command-formatters-DgADqnHa.js";
6
- import { a as resolveCodexNodeExecToolOverrides, i as resolveCodexNativeExecutionPolicy } from "./sandbox-guard-D1Wky__S.js";
7
- import { n as readCodexNotificationThreadId, r as readCodexNotificationTurnId } from "./notification-correlation-BGLixv5_.js";
1
+ import { H as CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE, W as isJsonObject, X as resolveCodexGatewayTimeoutWithGraceMs, l as releaseLeasedSharedCodexAppServerClient } from "./shared-client-DTbvH-XV.js";
2
+ import { O as readCodexPluginConfig } from "./session-binding-BXpo9ymA.js";
3
+ import { D as isForcedPrivateQaCodexRuntime, E as filterCodexDynamicToolsWithOpenClawShell, O as isSystemAgentOnlyCodexDynamicToolAllowlist, T as filterCodexDynamicTools, ct as readString$2, g as resolveCodexWebSearchPlan, i as invalidInlineImageText, k as normalizeCodexDynamicToolName, nt as readItemString, o as sanitizeInlineImageDataUrl, tt as readItem } from "./thread-lifecycle-DF53ogiA.js";
4
+ import { S as readCodexTurn } from "./transcript-mirror-DzsdNCf5.js";
5
+ import { r as formatCodexDisplayText } from "./command-formatters-C336eRVS.js";
6
+ import { a as resolveCodexNodeExecToolOverrides, i as resolveCodexNativeExecutionPolicy } from "./sandbox-guard-NuhpXmPB.js";
7
+ import { n as readCodexNotificationThreadId, r as readCodexNotificationTurnId } from "./notification-correlation-CU6_AnOb.js";
8
+ import path from "node:path";
8
9
  import { addTimerTimeoutGraceMs, asDateTimestampMs, finiteSecondsToTimerSafeMilliseconds, parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime";
9
- import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
10
- import { createHash } from "node:crypto";
11
- import { resolveAgentDir } from "openclaw/plugin-sdk/agent-runtime";
12
- import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
13
- import { asOptionalRecord, isRecord, normalizeTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
14
10
  import { HEARTBEAT_RESPONSE_TOOL_NAME, buildAgentHookContextChannelFields, buildEmbeddedAttemptToolRunContext, callGatewayTool, consumeAdjustedParamsForToolCall, consumePreExecutionBlockedToolCall, createAgentToolResultMiddlewareRunner, createCodexAppServerToolResultExtensionRunner, embeddedAgentLog, extractMessagingToolSend, extractMessagingToolSendResult, extractToolResultMediaArtifact, filterProviderNormalizableTools, filterToolResultMediaUrls, finalizeToolTerminalPresentation, formatApprovalDisplayPath, formatToolExecutionErrorMessage, getBeforeToolCallFailureDisposition, getChannelAgentToolMeta, getPluginToolMeta, hasNativeHookRelayInvocation, invokeNativeHookRelay, isDeliveredMessageToolOnlySourceReplyResult, isDeliveredMessagingToolResult, isHostScopedAgentToolActive, isMessagingTool, isMessagingToolSendAction, isReplaySafeToolCall, isSubagentSessionKey, isToolResultError, isToolWrappedWithBeforeToolCallHook, normalizeAgentRuntimeTools, normalizeHeartbeatToolResponse, projectRuntimeToolInputSchema, registerNativeHookRelay, resolveAttemptSpawnWorkspaceDir, resolveModelAuthMode, resolveNativeHookRelayDeferredToolApproval, resolveToolExecutionErrorKind, resolveToolResultFailureKind, runAgentHarnessAfterToolCallHook, runBeforeToolCallHook, sanitizeToolResult, setBeforeToolCallDiagnosticsEnabled, supportsModelTools, wrapToolWithBeforeToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
11
+ import { createHash } from "node:crypto";
12
+ import { resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
13
+ import { isRecord, normalizeTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
14
+ import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
15
15
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
16
16
  import { emitTrustedDiagnosticEvent, hasPendingInternalDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
17
17
  import { isToolAllowed } from "openclaw/plugin-sdk/sandbox";
18
+ import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
19
+ import { root } from "openclaw/plugin-sdk/file-access-runtime";
20
+ import { getMediaDir } from "openclaw/plugin-sdk/media-runtime";
18
21
  import { isApprovalNotFoundError } from "openclaw/plugin-sdk/error-runtime";
19
22
  import { normalizeOpenAIToolSchemas } from "openclaw/plugin-sdk/provider-tools";
20
23
  //#region extensions/codex/src/app-server/event-projector-items.ts
@@ -109,7 +112,7 @@ function shouldSynthesizeToolProgressForItem(item) {
109
112
  }
110
113
  }
111
114
  function shouldRecordNativeToolTranscript(item) {
112
- return shouldSynthesizeToolProgressForItem(item) && item.type !== "webSearch";
115
+ return shouldSynthesizeToolProgressForItem(item);
113
116
  }
114
117
  function isMutatingNativeToolItem(item) {
115
118
  if (item.type === "commandExecution") return true;
@@ -129,6 +132,18 @@ function shouldClearTerminalPresentationForNativeItem(item) {
129
132
  }
130
133
  //#endregion
131
134
  //#region extensions/codex/src/app-server/dynamic-tool-response-state.ts
135
+ /** Retains the host-owned app preview without adding it to Codex's response payload. */
136
+ function withDynamicToolTranscriptDetails(response, details) {
137
+ if (!details || typeof details !== "object" || Array.isArray(details)) return response;
138
+ const mcpAppPreview = details.mcpAppPreview;
139
+ if (!mcpAppPreview || typeof mcpAppPreview !== "object" || Array.isArray(mcpAppPreview)) return response;
140
+ Object.defineProperty(response, "transcriptDetails", {
141
+ configurable: true,
142
+ enumerable: false,
143
+ value: { mcpAppPreview }
144
+ });
145
+ return response;
146
+ }
132
147
  function withDynamicToolTerminalResolution(response, terminalResolution) {
133
148
  if (terminalResolution) {
134
149
  Object.defineProperties(response, {
@@ -444,13 +459,16 @@ function toCodexDynamicToolProtocolResponse(response) {
444
459
  }
445
460
  /** Adds async-started progress details when a tool result continues out of band. */
446
461
  function toCodexDynamicToolProgressResponse(response, protocolResponse) {
447
- if (response.asyncStarted !== true) return protocolResponse;
462
+ const transcriptDetails = response.transcriptDetails;
463
+ if (response.asyncStarted !== true && transcriptDetails === void 0) return protocolResponse;
448
464
  return {
449
465
  ...protocolResponse,
450
- details: {
466
+ ...transcriptDetails ? { details: transcriptDetails } : {},
467
+ ...response.asyncStarted === true ? { details: {
468
+ ...transcriptDetails,
451
469
  async: true,
452
470
  status: "started"
453
- }
471
+ } } : {}
454
472
  };
455
473
  }
456
474
  /** Decides whether a terminal dynamic tool response can release the Codex turn. */
@@ -590,6 +608,19 @@ const CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS = 1e3;
590
608
  const CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC = 10;
591
609
  const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS = 1e4;
592
610
  const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS = 5e3;
611
+ const CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID = {
612
+ exec: [
613
+ "Bash",
614
+ "exec",
615
+ "exec_command"
616
+ ],
617
+ apply_patch: [
618
+ "apply_patch",
619
+ "Write",
620
+ "Edit"
621
+ ],
622
+ spawn_agent: ["spawn_agent", "Agent"]
623
+ };
593
624
  /** Defers relay unregister so late native hook subprocesses can still resolve. */
594
625
  function scheduleCodexNativeHookRelayUnregister(params) {
595
626
  let pending;
@@ -648,6 +679,7 @@ function createCodexNativeHookRelay(params) {
648
679
  runId: params.runId,
649
680
  ...params.channelId ? { channelId: params.channelId } : {},
650
681
  ...params.requester ? { requester: params.requester } : {},
682
+ ...params.approvalContext ? { approvalContext: params.approvalContext } : {},
651
683
  allowedEvents: params.events,
652
684
  preToolUseLoopDetection: params.loopDetectionPreToolUseRelay,
653
685
  ttlMs: resolveCodexNativeHookRelayTtlMs({
@@ -716,18 +748,23 @@ function buildCodexNativeHookRelayConfig(params) {
716
748
  }
717
749
  const timeout = normalizeHookTimeoutSec(params.hookTimeoutSec);
718
750
  const command = params.relay.commandForEvent(event, { timeoutMs: resolveCodexNativeHookRelayCommandTimeoutMs(timeout) });
719
- config[`hooks.${codexEvent}`] = [{ hooks: [{
720
- type: "command",
721
- command,
722
- timeout,
723
- async: false,
724
- statusMessage: "OpenClaw native hook relay"
725
- }] }];
751
+ const matcher = selectedNoopPreToolUse ? void 0 : buildCodexNativeToolMatcher(params.relay.toolMatcherForEvent(event));
752
+ config[`hooks.${codexEvent}`] = [{
753
+ ...matcher ? { matcher } : {},
754
+ hooks: [{
755
+ type: "command",
756
+ command,
757
+ timeout,
758
+ async: false,
759
+ statusMessage: "OpenClaw native hook relay"
760
+ }]
761
+ }];
726
762
  const state = {
727
763
  enabled: true,
728
764
  trusted_hash: codexCommandHookTrustedHash({
729
765
  event,
730
766
  command,
767
+ matcher,
731
768
  timeout,
732
769
  statusMessage: "OpenClaw native hook relay"
733
770
  })
@@ -755,9 +792,26 @@ function resolveCodexNativeHookRelayCommandTimeoutMs(hookTimeoutSec) {
755
792
  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)));
756
793
  return Math.max(1, parentTimeoutMs - parentMarginMs);
757
794
  }
795
+ function buildCodexNativeToolMatcher(toolNames) {
796
+ if (toolNames === void 0) return;
797
+ if (toolNames.length === 0) throw new TypeError("Codex native hook matcher requires at least one tool name");
798
+ const nativeNames = /* @__PURE__ */ new Set();
799
+ let hasCustomToolName = false;
800
+ for (const toolName of toolNames) {
801
+ const canonicalToolName = toolName.trim();
802
+ if (!canonicalToolName || canonicalToolName === "*") throw new TypeError("Codex native hook matcher requires canonical OpenClaw tool ids");
803
+ const nativeAliases = CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID[canonicalToolName];
804
+ if (!nativeAliases) hasCustomToolName = true;
805
+ for (const nativeName of nativeAliases ?? [canonicalToolName]) nativeNames.add(nativeName);
806
+ }
807
+ const sortedNames = Array.from(nativeNames).toSorted();
808
+ if (!hasCustomToolName && sortedNames.every((toolName) => /^[A-Za-z0-9_]+$/.test(toolName))) return sortedNames.join("|");
809
+ return `(?i)^(?:${sortedNames.map((toolName) => toolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})$`;
810
+ }
758
811
  function codexCommandHookTrustedHash(params) {
759
812
  const identity = {
760
813
  event_name: CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[params.event],
814
+ ...params.matcher ? { matcher: params.matcher } : {},
761
815
  hooks: [{
762
816
  async: false,
763
817
  command: params.command,
@@ -970,6 +1024,322 @@ var CodexNativeToolLifecycleProjector = class {
970
1024
  }
971
1025
  };
972
1026
  //#endregion
1027
+ //#region extensions/codex/src/app-server/remote-workspace-path.ts
1028
+ /** Projects a gateway workspace path into the remote Codex execution workspace. */
1029
+ function mapCodexAppServerRemoteWorkspacePath(params) {
1030
+ if (!params.remoteWorkspaceRoot) return params.value;
1031
+ const localRoot = normalizeWorkspaceMatchPath(params.localWorkspaceRoot);
1032
+ const remoteRoot = normalizeWorkspaceMatchPath(params.remoteWorkspaceRoot);
1033
+ const normalizedValue = normalizeWorkspaceMatchPath(params.value);
1034
+ if (!localRoot || !remoteRoot) throw new Error("Codex remoteWorkspaceRoot requires non-empty workspace roots.");
1035
+ if (normalizedValue === localRoot) return remoteRoot;
1036
+ const prefix = `${localRoot}/`;
1037
+ if (!normalizedValue.startsWith(prefix)) throw new Error(`Codex remoteWorkspaceRoot is configured but cwd ${params.value} is outside OpenClaw workspace root ${params.localWorkspaceRoot}; refusing to send a gateway-local cwd to the remote Codex app-server.`);
1038
+ return joinRemoteWorkspacePath(remoteRoot, normalizedValue.slice(prefix.length));
1039
+ }
1040
+ /** Maps a remote workspace artifact back into the corresponding gateway workspace. */
1041
+ function mapCodexAppServerLocalWorkspacePath(params) {
1042
+ if (!params.remoteWorkspaceRoot) return params.value;
1043
+ const localRoot = normalizeWorkspaceMatchPath(params.localWorkspaceRoot);
1044
+ const remoteRoot = normalizeWorkspaceMatchPath(params.remoteWorkspaceRoot);
1045
+ if (!localRoot || !remoteRoot) throw new Error("Codex remoteWorkspaceRoot requires non-empty workspace roots.");
1046
+ const normalizedValue = normalizeWorkspaceMatchPath(params.value);
1047
+ if (!normalizedValue || isCodexPassThroughMediaSource(normalizedValue)) return params.value;
1048
+ const usesWindowsPaths = /^[a-z]:\//iu.test(remoteRoot) || remoteRoot.startsWith("//");
1049
+ const matchValue = usesWindowsPaths ? normalizedValue.toLowerCase() : normalizedValue;
1050
+ const matchRoot = usesWindowsPaths ? remoteRoot.toLowerCase() : remoteRoot;
1051
+ if (matchValue === matchRoot) return params.localWorkspaceRoot;
1052
+ const prefix = matchRoot.endsWith("/") ? matchRoot : `${matchRoot}/`;
1053
+ const isRemoteWorkspacePath = matchValue.startsWith(prefix);
1054
+ if (!isRemoteWorkspacePath && isAbsoluteWorkspacePath(normalizedValue)) throw new Error(`Codex remote workspace artifact ${params.value} is outside ${params.remoteWorkspaceRoot}.`);
1055
+ const suffixSegments = (isRemoteWorkspacePath ? normalizedValue.slice(prefix.length) : normalizedValue).split("/");
1056
+ if (suffixSegments.some((segment) => segment === "..")) throw new Error(`Codex remote workspace artifact ${params.value} must stay inside ${params.remoteWorkspaceRoot}.`);
1057
+ return path.join(params.localWorkspaceRoot, ...suffixSegments.filter((segment) => segment !== "."));
1058
+ }
1059
+ function normalizeWorkspaceMatchPath(value) {
1060
+ const normalized = value.replace(/\\/gu, "/");
1061
+ if (/^[a-z]:\/$/iu.test(normalized)) return normalized;
1062
+ return normalized.length > 1 ? normalized.replace(/[\\/]+$/u, "") : normalized;
1063
+ }
1064
+ /** Keeps remote URLs and opaque managed media references out of workspace path mapping. */
1065
+ function isCodexPassThroughMediaSource(value) {
1066
+ return /^(?:https?|mxc|buffer|media):\/\//iu.test(value) || /^data:/iu.test(value);
1067
+ }
1068
+ function isAbsoluteWorkspacePath(value) {
1069
+ return value.startsWith("/") || /^[a-z]:\//iu.test(value) || /^[a-z][a-z0-9+.-]*:/iu.test(value);
1070
+ }
1071
+ function joinRemoteWorkspacePath(remoteRoot, suffix) {
1072
+ return remoteRoot.endsWith("/") ? `${remoteRoot}${suffix}` : `${remoteRoot}/${suffix}`;
1073
+ }
1074
+ //#endregion
1075
+ //#region extensions/codex/src/app-server/remote-workspace-media.ts
1076
+ const REMOTE_WORKSPACE_MEDIA_TIMEOUT_MS = 6e4;
1077
+ const REMOTE_WORKSPACE_MEDIA_MAX_BYTES = 64 * 1024 * 1024;
1078
+ const REMOTE_WORKSPACE_MEDIA_MAX_ATTACHMENTS = 16;
1079
+ const CODEX_REMOTE_MEDIA_CHUNK_BYTES = 512 * 1024;
1080
+ const CODEX_REMOTE_COMMAND_DEFAULT_OUTPUT_BYTES = 1024 * 1024;
1081
+ const CODEX_BOUNDED_REMOTE_FILE_READER = [
1082
+ "try{",
1083
+ "const fs=require(\"node:fs\");",
1084
+ "const path=require(\"node:path\");",
1085
+ "const file=process.argv[1];",
1086
+ "const max=Number(process.argv[2]);",
1087
+ "const offset=Number(process.argv[3]);",
1088
+ "const chunk=Number(process.argv[4]);",
1089
+ "const workspace=process.argv[5];",
1090
+ "if(!Number.isSafeInteger(max)||max<0)throw Error(\"invalid media byte limit\");",
1091
+ "if(!Number.isSafeInteger(offset)||offset<0||!Number.isSafeInteger(chunk)||chunk<=0)throw Error(\"invalid media chunk\");",
1092
+ "if(fs.lstatSync(file).isSymbolicLink())throw Error(\"symbolic links are not allowed\");",
1093
+ "const noFollow=fs.constants.O_NOFOLLOW??0;",
1094
+ "const fd=fs.openSync(file,fs.constants.O_RDONLY|noFollow);",
1095
+ "try{",
1096
+ "const before=fs.fstatSync(fd);",
1097
+ "if(!before.isFile())throw Error(\"not a regular file\");",
1098
+ "if(workspace){",
1099
+ "const descriptor=process.platform===\"linux\"?fs.realpathSync(`/proc/self/fd/${fd}`):fs.realpathSync(file);",
1100
+ "const relative=path.relative(fs.realpathSync(workspace),descriptor);",
1101
+ "if(!relative||relative===\"..\"||relative.startsWith(`..${path.sep}`)||path.isAbsolute(relative))throw Error(\"file escapes remote workspace\");",
1102
+ "const verified=fs.statSync(descriptor);",
1103
+ "if(verified.dev!==before.dev||verified.ino!==before.ino)throw Error(\"file changed while being opened\");",
1104
+ "}",
1105
+ "if(before.size>max)throw Error(`file exceeds limit of ${max} bytes`);",
1106
+ "if(offset>before.size)throw Error(\"file changed while being read\");",
1107
+ "const expected=Math.min(chunk,before.size-offset);",
1108
+ "const buffer=Buffer.allocUnsafe(expected);",
1109
+ "let total=0;",
1110
+ "while(total<buffer.length){",
1111
+ "const count=fs.readSync(fd,buffer,total,buffer.length-total,offset+total);",
1112
+ "if(count===0)break;total+=count;",
1113
+ "}",
1114
+ "if(total!==expected)throw Error(\"file changed while being read\");",
1115
+ "const after=fs.fstatSync(fd);",
1116
+ "const revision=stat=>[stat.dev,stat.ino,stat.size,stat.mtimeMs,stat.ctimeMs].join(\":\");",
1117
+ "if(!after.isFile()||revision(after)!==revision(before))throw Error(\"file changed while being read\");",
1118
+ "process.stdout.write(JSON.stringify({dataBase64:buffer.toString(\"base64\"),size:before.size,revision:revision(before)}));",
1119
+ "}finally{fs.closeSync(fd)}",
1120
+ "}catch(error){process.stderr.write(error instanceof Error?error.message:String(error));process.exitCode=1}"
1121
+ ].join("");
1122
+ const MESSAGE_MEDIA_KEYS = [
1123
+ "media",
1124
+ "mediaUrl",
1125
+ "media_url",
1126
+ "path",
1127
+ "filePath",
1128
+ "fileUrl",
1129
+ "imageUrl",
1130
+ "image_url"
1131
+ ];
1132
+ const MESSAGE_MEDIA_ARRAY_KEYS = [
1133
+ "mediaUrls",
1134
+ "media_urls",
1135
+ "imageUrls",
1136
+ "image_urls"
1137
+ ];
1138
+ const ATTACHMENT_MEDIA_KEYS = [
1139
+ "media",
1140
+ "mediaUrl",
1141
+ "path",
1142
+ "filePath",
1143
+ "fileUrl",
1144
+ "url"
1145
+ ];
1146
+ /** Reads actual remote bytes with a cap enforced by Codex before transport. */
1147
+ async function readBoundedCodexRemoteWorkspaceFile(params) {
1148
+ if (!Number.isSafeInteger(params.maxBytes) || params.maxBytes < 0) throw new Error("Codex remote workspace upload requires a valid media byte limit.");
1149
+ params.signal?.throwIfAborted();
1150
+ const chunks = [];
1151
+ let offset = 0;
1152
+ let expectedSize;
1153
+ let expectedRevision;
1154
+ const startedAt = Date.now();
1155
+ do {
1156
+ params.signal?.throwIfAborted();
1157
+ const timeoutMs = params.timeoutMs === void 0 ? void 0 : params.timeoutMs - (Date.now() - startedAt);
1158
+ if (timeoutMs !== void 0 && timeoutMs <= 0) throw new Error("Codex remote workspace file transfer timed out.");
1159
+ let response;
1160
+ try {
1161
+ response = await params.client.request("command/exec", {
1162
+ command: [
1163
+ "node",
1164
+ "-e",
1165
+ CODEX_BOUNDED_REMOTE_FILE_READER,
1166
+ "--",
1167
+ params.path,
1168
+ String(params.maxBytes),
1169
+ String(offset),
1170
+ String(CODEX_REMOTE_MEDIA_CHUNK_BYTES),
1171
+ ...params.workspaceRoot ? [params.workspaceRoot] : []
1172
+ ],
1173
+ env: {
1174
+ NODE_OPTIONS: null,
1175
+ NODE_PATH: null
1176
+ },
1177
+ ...timeoutMs === void 0 ? {} : { timeoutMs }
1178
+ }, {
1179
+ signal: params.signal,
1180
+ timeoutMs
1181
+ });
1182
+ } catch (error) {
1183
+ if (error instanceof Error && /failed to spawn|executable.*not found|\bENOENT\b/iu.test(error.message)) throw new Error("Codex remote workspace file transfer requires Node.js on the remote app-server host.", { cause: error });
1184
+ throw error;
1185
+ }
1186
+ if (!response || response.exitCode !== 0) {
1187
+ const detail = typeof response?.stderr === "string" ? response.stderr.trim() : "";
1188
+ throw new Error(`Codex remote workspace artifact could not be read: ${params.path}${detail ? `: ${detail}` : ""}`);
1189
+ }
1190
+ if (typeof response.stdout !== "string" || response.stdout.length > CODEX_REMOTE_COMMAND_DEFAULT_OUTPUT_BYTES) throw new Error("Codex remote workspace artifact exceeded the native command output cap.");
1191
+ let payload;
1192
+ try {
1193
+ payload = JSON.parse(response.stdout);
1194
+ } catch {
1195
+ throw new Error("Codex remote workspace artifact returned invalid chunk data.");
1196
+ }
1197
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) throw new Error("Codex remote workspace artifact returned invalid chunk data.");
1198
+ const chunk = payload;
1199
+ if (typeof chunk.dataBase64 !== "string" || !Number.isSafeInteger(chunk.size) || chunk.size < 0 || chunk.size > params.maxBytes || typeof chunk.revision !== "string" || !chunk.revision) throw new Error("Codex remote workspace artifact returned invalid or oversized chunk data.");
1200
+ if (expectedSize === void 0) {
1201
+ expectedSize = chunk.size;
1202
+ expectedRevision = chunk.revision;
1203
+ }
1204
+ if (chunk.size !== expectedSize || chunk.revision !== expectedRevision) throw new Error("Codex remote workspace artifact changed during chunked transfer.");
1205
+ const remainingBytes = expectedSize - offset;
1206
+ const expectedChunkBytes = Math.min(CODEX_REMOTE_MEDIA_CHUNK_BYTES, remainingBytes);
1207
+ if (chunk.dataBase64.length > Math.ceil(expectedChunkBytes / 3) * 4) throw new Error("Codex remote workspace artifact returned oversized chunk data.");
1208
+ const buffer = Buffer.from(chunk.dataBase64, "base64");
1209
+ if (buffer.byteLength !== expectedChunkBytes || buffer.toString("base64") !== chunk.dataBase64) throw new Error("Codex remote workspace artifact returned invalid chunk data.");
1210
+ chunks.push(buffer);
1211
+ offset += buffer.byteLength;
1212
+ } while (offset < (expectedSize ?? 0));
1213
+ return { dataBase64: Buffer.concat(chunks, offset).toString("base64") };
1214
+ }
1215
+ /** Stages authoritative bounded remote bytes into immutable Gateway-owned media. */
1216
+ async function prepareCodexRemoteWorkspaceMessageMedia(params) {
1217
+ const { localWorkspaceRoot, remoteWorkspaceRoot } = params;
1218
+ if (!localWorkspaceRoot || !remoteWorkspaceRoot) return params.args;
1219
+ const remotePathsByLocalPath = /* @__PURE__ */ new Map();
1220
+ const gatewayManagedPaths = /* @__PURE__ */ new Set();
1221
+ const gatewayMediaRoot = getMediaDir();
1222
+ let attachmentEntries = 0;
1223
+ const mapMediaPath = (value) => {
1224
+ if (typeof value !== "string") return value;
1225
+ if (isGatewayManagedMediaPath(value, gatewayMediaRoot)) {
1226
+ attachmentEntries += 1;
1227
+ gatewayManagedPaths.add(value);
1228
+ return value;
1229
+ }
1230
+ const mapped = mapCodexAppServerLocalWorkspacePath({
1231
+ value,
1232
+ localWorkspaceRoot,
1233
+ remoteWorkspaceRoot
1234
+ });
1235
+ if (value.trim() && !isCodexPassThroughMediaSource(value)) {
1236
+ attachmentEntries += 1;
1237
+ remotePathsByLocalPath.set(mapped, mapCodexAppServerRemoteWorkspacePath({
1238
+ value: mapped,
1239
+ localWorkspaceRoot,
1240
+ remoteWorkspaceRoot
1241
+ }));
1242
+ }
1243
+ return mapped;
1244
+ };
1245
+ let mappedArgs = params.args;
1246
+ const setMappedValue = (key, value) => {
1247
+ if (value === params.args[key]) return;
1248
+ if (mappedArgs === params.args) mappedArgs = { ...params.args };
1249
+ mappedArgs[key] = value;
1250
+ };
1251
+ for (const key of MESSAGE_MEDIA_KEYS) setMappedValue(key, mapMediaPath(params.args[key]));
1252
+ for (const key of MESSAGE_MEDIA_ARRAY_KEYS) {
1253
+ const value = params.args[key];
1254
+ if (Array.isArray(value)) {
1255
+ const mapped = value.map(mapMediaPath);
1256
+ if (mapped.some((entry, index) => entry !== value[index])) setMappedValue(key, mapped);
1257
+ }
1258
+ }
1259
+ if (Array.isArray(params.args.attachments)) {
1260
+ const attachments = params.args.attachments;
1261
+ const mapped = attachments.map((attachment) => {
1262
+ if (!attachment || typeof attachment !== "object" || Array.isArray(attachment)) return attachment;
1263
+ const record = attachment;
1264
+ let mappedAttachment = record;
1265
+ for (const key of ATTACHMENT_MEDIA_KEYS) {
1266
+ const value = mapMediaPath(record[key]);
1267
+ if (value !== record[key]) {
1268
+ if (mappedAttachment === record) mappedAttachment = { ...record };
1269
+ mappedAttachment[key] = value;
1270
+ }
1271
+ }
1272
+ return mappedAttachment;
1273
+ });
1274
+ if (mapped.some((attachment, index) => attachment !== attachments[index])) setMappedValue("attachments", mapped);
1275
+ }
1276
+ if (attachmentEntries > REMOTE_WORKSPACE_MEDIA_MAX_ATTACHMENTS) throw new Error(`Codex remote workspace upload exceeds the ${REMOTE_WORKSPACE_MEDIA_MAX_ATTACHMENTS}-attachment limit.`);
1277
+ for (const managedPath of gatewayManagedPaths) await assertGatewayManagedMediaPath(managedPath, gatewayMediaRoot);
1278
+ if (remotePathsByLocalPath.size === 0) return mappedArgs;
1279
+ const readRemoteFile = params.readRemoteFile;
1280
+ if (!readRemoteFile) throw new Error("Codex remote workspace file transfer requires an active app-server client.");
1281
+ const maxBytes = params.maxBytes ?? REMOTE_WORKSPACE_MEDIA_MAX_BYTES;
1282
+ const timeoutMs = params.timeoutMs ?? REMOTE_WORKSPACE_MEDIA_TIMEOUT_MS;
1283
+ const deadline = Date.now() + timeoutMs;
1284
+ const stagedPaths = /* @__PURE__ */ new Map();
1285
+ let totalBytes = 0;
1286
+ for (const [localPath, remotePath] of remotePathsByLocalPath) {
1287
+ params.signal?.throwIfAborted();
1288
+ const remainingBytes = maxBytes - totalBytes;
1289
+ const remainingMs = deadline - Date.now();
1290
+ if (remainingMs <= 0) throw new Error("Codex remote workspace attachment batch timed out.");
1291
+ const response = await readRemoteFile({
1292
+ path: remotePath,
1293
+ maxBytes: remainingBytes,
1294
+ workspaceRoot: remoteWorkspaceRoot,
1295
+ signal: params.signal,
1296
+ timeoutMs: remainingMs
1297
+ });
1298
+ if (!response || typeof response.dataBase64 !== "string") throw new Error(`Codex remote workspace artifact returned no file data: ${remotePath}`);
1299
+ if (response.dataBase64.length > Math.ceil(remainingBytes / 3) * 4) throw new Error(`Codex remote workspace artifact exceeds the limit of ${remainingBytes} bytes.`);
1300
+ const remoteBuffer = Buffer.from(response.dataBase64, "base64");
1301
+ if (remoteBuffer.byteLength > remainingBytes || remoteBuffer.toString("base64") !== response.dataBase64) throw new Error(`Codex remote workspace artifact returned invalid or oversized file data: ${remotePath}`);
1302
+ totalBytes += remoteBuffer.byteLength;
1303
+ const saved = await saveMediaBuffer(remoteBuffer, void 0, "outbound", maxBytes, path.basename(remotePath));
1304
+ stagedPaths.set(localPath, saved.path);
1305
+ }
1306
+ return mapMessageMediaValues(mappedArgs, (value) => stagedPaths.get(value) ?? value);
1307
+ }
1308
+ function isGatewayManagedMediaPath(value, mediaRoot) {
1309
+ if (!path.isAbsolute(value)) return false;
1310
+ const relativePath = path.relative(mediaRoot, value);
1311
+ return Boolean(relativePath && relativePath !== ".." && !relativePath.startsWith(`..${path.sep}`) && !path.isAbsolute(relativePath));
1312
+ }
1313
+ async function assertGatewayManagedMediaPath(value, mediaRoot) {
1314
+ const opened = await (await root(mediaRoot, { symlinks: "reject" })).open(path.relative(mediaRoot, value), { symlinks: "reject" });
1315
+ try {
1316
+ if (!(await opened.handle.stat()).isFile()) throw new Error(`Codex Gateway-managed media is not a regular file: ${value}`);
1317
+ } finally {
1318
+ await opened[Symbol.asyncDispose]();
1319
+ }
1320
+ }
1321
+ function mapMessageMediaValues(args, mapValue) {
1322
+ const mapped = { ...args };
1323
+ for (const key of MESSAGE_MEDIA_KEYS) {
1324
+ const value = mapped[key];
1325
+ if (typeof value === "string") mapped[key] = mapValue(value);
1326
+ }
1327
+ for (const key of MESSAGE_MEDIA_ARRAY_KEYS) {
1328
+ const value = mapped[key];
1329
+ if (Array.isArray(value)) mapped[key] = value.map((entry) => typeof entry === "string" ? mapValue(entry) : entry);
1330
+ }
1331
+ if (Array.isArray(mapped.attachments)) mapped.attachments = mapped.attachments.map((attachment) => {
1332
+ if (!attachment || typeof attachment !== "object" || Array.isArray(attachment)) return attachment;
1333
+ const record = { ...attachment };
1334
+ for (const key of ATTACHMENT_MEDIA_KEYS) {
1335
+ const value = record[key];
1336
+ if (typeof value === "string") record[key] = mapValue(value);
1337
+ }
1338
+ return record;
1339
+ });
1340
+ return mapped;
1341
+ }
1342
+ //#endregion
973
1343
  //#region extensions/codex/src/app-server/dynamic-tool-build-state.ts
974
1344
  /** Mutable dependency seam shared by dynamic-tool construction and its behavioral tests. */
975
1345
  const dynamicToolBuildState = {};
@@ -1178,9 +1548,11 @@ async function buildDynamicTools(input) {
1178
1548
  }
1179
1549
  const toolBuildStages = createCodexDynamicToolBuildStageTracker({ enabled: input.profilerEnabled });
1180
1550
  const modelHasVision = params.model.input?.includes("image") ?? false;
1181
- const agentDir = params.agentDir ?? resolveAgentDir(params.config ?? {}, input.sessionAgentId);
1182
- const { createOpenClawCodingTools: defaultCreateOpenClawCodingTools, resolveWebSearchToolPolicy } = await import("openclaw/plugin-sdk/agent-harness");
1183
- const createOpenClawCodingTools = dynamicToolBuildState.openClawCodingToolsFactory ?? defaultCreateOpenClawCodingTools;
1551
+ const agentDir = params.agentDir ?? resolveAgentDir$1(params.config ?? {}, input.sessionAgentId);
1552
+ const injectedOpenClawCodingToolsFactory = dynamicToolBuildState.openClawCodingToolsFactory;
1553
+ let agentHarnessModule;
1554
+ const loadAgentHarnessModule = async () => agentHarnessModule ??= await import("openclaw/plugin-sdk/agent-harness");
1555
+ const createOpenClawCodingTools = injectedOpenClawCodingToolsFactory ?? (await loadAgentHarnessModule()).createOpenClawCodingTools;
1184
1556
  toolBuildStages.mark("load-agent-harness-tools");
1185
1557
  const sessionKeys = resolveOpenClawCodingToolsSessionKeys(params, input.sandboxSessionKey);
1186
1558
  const nativeExecutionPolicy = resolveCodexNativeExecutionPolicyForDynamicTools(input);
@@ -1214,6 +1586,7 @@ async function buildDynamicTools(input) {
1214
1586
  senderIsOwner: params.senderIsOwner,
1215
1587
  inputProvenance: params.inputProvenance,
1216
1588
  trustedInternalHandoff: params.trustedInternalHandoff,
1589
+ scheduledToolPolicy: params.scheduledToolPolicy,
1217
1590
  allowGatewaySubagentBinding: params.allowGatewaySubagentBinding || isForcedPrivateQaCodexRuntime(),
1218
1591
  ...sessionKeys,
1219
1592
  sessionId: params.sessionId,
@@ -1290,7 +1663,8 @@ async function buildDynamicTools(input) {
1290
1663
  });
1291
1664
  toolBuildStages.mark("vision-filtering");
1292
1665
  const webSearchPresent = visionFilteredTools.some((tool) => tool.name === "web_search");
1293
- const webSearchPolicy = resolveWebSearchToolPolicy({
1666
+ const persistentCodexWebSearchSurface = params.config?.tools?.web?.search?.enabled !== false && !(input.pluginConfig.codexDynamicToolsExclude ?? []).some((name) => normalizeCodexDynamicToolName(name) === "web_search");
1667
+ const webSearchPolicy = webSearchPresent || persistentCodexWebSearchSurface ? (await loadAgentHarnessModule()).resolveWebSearchToolPolicy({
1294
1668
  config: params.config,
1295
1669
  modelProvider: params.model.provider,
1296
1670
  modelId: params.modelId,
@@ -1308,10 +1682,13 @@ async function buildDynamicTools(input) {
1308
1682
  senderUsername: params.senderUsername,
1309
1683
  senderE164: params.senderE164,
1310
1684
  inputProvenance: params.inputProvenance,
1311
- trustedInternalHandoff: params.trustedInternalHandoff
1312
- });
1685
+ trustedInternalHandoff: params.trustedInternalHandoff,
1686
+ scheduledToolPolicy: params.scheduledToolPolicy
1687
+ }) : {
1688
+ allowed: false,
1689
+ persistentAllowed: false
1690
+ };
1313
1691
  const transientWebSearchRestriction = !webSearchPolicy.allowed && webSearchPolicy.persistentAllowed || isCodexMemoryFlushRun(params);
1314
- const persistentCodexWebSearchSurface = params.config?.tools?.web?.search?.enabled !== false && !(input.pluginConfig.codexDynamicToolsExclude ?? []).some((name) => normalizeCodexDynamicToolName(name) === "web_search");
1315
1692
  input.onPersistentWebSearchPolicyResolved?.(webSearchPresent || persistentCodexWebSearchSurface && transientWebSearchRestriction && webSearchPolicy.persistentAllowed);
1316
1693
  const filteredTools = filterCodexDynamicToolsForAllowlist(visionFilteredTools, includeForcedCodexDynamicToolAllow(params.toolsAllow, messagePolicyParams));
1317
1694
  toolBuildStages.mark("allowlist-filter");
@@ -1424,27 +1801,6 @@ function resolveCodexAppServerExecutionCwd(params) {
1424
1801
  remoteWorkspaceRoot: params.remoteWorkspaceRoot
1425
1802
  });
1426
1803
  }
1427
- /** Projects a local OpenClaw workspace cwd into the remote Codex app-server workspace root. */
1428
- function mapCodexAppServerRemoteWorkspacePath(params) {
1429
- if (!params.remoteWorkspaceRoot) return params.value;
1430
- const localRoot = normalizeRemoteWorkspaceMatchPath(params.localWorkspaceRoot);
1431
- const remoteRoot = normalizeRemoteWorkspaceMatchPath(params.remoteWorkspaceRoot);
1432
- const normalizedValue = normalizeRemoteWorkspaceMatchPath(params.value);
1433
- if (!localRoot || !remoteRoot) throw new Error("Codex remoteWorkspaceRoot requires non-empty workspace roots.");
1434
- if (normalizedValue === localRoot) return remoteRoot;
1435
- const prefix = `${localRoot}/`;
1436
- if (!normalizedValue.startsWith(prefix)) throw new Error(`Codex remoteWorkspaceRoot is configured but cwd ${params.value} is outside OpenClaw workspace root ${params.localWorkspaceRoot}; refusing to send a gateway-local cwd to the remote Codex app-server.`);
1437
- return joinRemoteWorkspacePath(remoteRoot, normalizedValue.slice(prefix.length));
1438
- }
1439
- function normalizeRemoteWorkspaceMatchPath(value) {
1440
- return trimTrailingPathSeparator(value.replace(/\\/gu, "/"));
1441
- }
1442
- function trimTrailingPathSeparator(value) {
1443
- return value.length > 1 ? value.replace(/[\\/]+$/u, "") : value;
1444
- }
1445
- function joinRemoteWorkspacePath(remoteRoot, suffix) {
1446
- return remoteRoot === "/" ? `/${suffix}` : `${remoteRoot}/${suffix}`;
1447
- }
1448
1804
  /** Converts OpenClaw sandbox networking into Codex's external-sandbox policy shape. */
1449
1805
  function resolveCodexExternalSandboxPolicyForOpenClawSandbox(sandbox) {
1450
1806
  return {
@@ -1586,7 +1942,26 @@ function settleCodexSourceReplyFinality(owner, turnSucceeded) {
1586
1942
  return turnSucceeded && intents.some((intent) => intent.record.sourceReplyFinal === true);
1587
1943
  }
1588
1944
  //#endregion
1945
+ //#region extensions/codex/src/app-server/hook-requester.ts
1946
+ /** Rebuilds the host-proven requester identity shared by native and bridged tool hooks. */
1947
+ function buildCodexHookRequester(params) {
1948
+ const channel = params.messageChannel ?? params.messageProvider;
1949
+ const requester = {
1950
+ ...channel ? { channel } : {},
1951
+ ...params.agentAccountId ? { accountId: params.agentAccountId } : {},
1952
+ ...params.senderId ? { senderId: params.senderId } : {},
1953
+ ...params.senderIsOwner !== void 0 ? { senderIsOwner: params.senderIsOwner } : {},
1954
+ ...params.memberRoleIds?.length ? { roleIds: [...params.memberRoleIds] } : {}
1955
+ };
1956
+ return Object.keys(requester).length > 0 ? requester : void 0;
1957
+ }
1958
+ //#endregion
1589
1959
  //#region extensions/codex/src/app-server/provider-capabilities.ts
1960
+ function resolveOverriddenProviderWebSearchSupport(modelProviderOverride) {
1961
+ const provider = modelProviderOverride?.trim().toLowerCase();
1962
+ if (!provider) return;
1963
+ return provider === "openai" ? "supported" : "unsupported";
1964
+ }
1590
1965
  async function readConfiguredProviderWebSearchSupport(params) {
1591
1966
  return (await params.client.request("modelProvider/capabilities/read", {}, {
1592
1967
  timeoutMs: params.timeoutMs,
@@ -1594,9 +1969,8 @@ async function readConfiguredProviderWebSearchSupport(params) {
1594
1969
  })).webSearch ? "supported" : "unsupported";
1595
1970
  }
1596
1971
  async function resolveCodexProviderWebSearchSupportForClient(params) {
1597
- const modelProviderOverride = params.modelProviderOverride?.trim().toLowerCase();
1598
- if (modelProviderOverride === "openai") return "supported";
1599
- if (modelProviderOverride) return "unsupported";
1972
+ const overrideSupport = resolveOverriddenProviderWebSearchSupport(params.modelProviderOverride);
1973
+ if (overrideSupport) return overrideSupport;
1600
1974
  try {
1601
1975
  return await readConfiguredProviderWebSearchSupport(params);
1602
1976
  } catch {
@@ -1604,6 +1978,8 @@ async function resolveCodexProviderWebSearchSupportForClient(params) {
1604
1978
  }
1605
1979
  }
1606
1980
  async function resolveCodexProviderWebSearchSupport(params) {
1981
+ const overrideSupport = resolveOverriddenProviderWebSearchSupport(params.modelProviderOverride);
1982
+ if (overrideSupport) return overrideSupport;
1607
1983
  let client;
1608
1984
  try {
1609
1985
  client = await params.clientFactory({
@@ -2383,6 +2759,7 @@ const PERMISSION_VALUE_MAX_LENGTH = 48;
2383
2759
  const COMMAND_PREVIEW_WITH_DETAILS_MAX_LENGTH = 80;
2384
2760
  const APPROVAL_PREVIEW_SCAN_MAX_LENGTH = 4096;
2385
2761
  const APPROVAL_PREVIEW_OMITTED = "[preview truncated or unsafe content omitted]";
2762
+ const CONCRETE_TOOL_AUTO_APPROVAL_METHODS = /* @__PURE__ */ new Set(["item/commandExecution/requestApproval", "item/fileChange/requestApproval"]);
2386
2763
  const ANSI_OSC_SEQUENCE_RE = new RegExp(String.raw`(?:\u001b]|\u009d)[^\u001b\u009c\u0007]*(?:\u0007|\u001b\\|\u009c)`, "g");
2387
2764
  const ANSI_CONTROL_SEQUENCE_RE = new RegExp(String.raw`(?:\u001b\[[0-?]*[ -/]*[@-~]|\u009b[0-?]*[ -/]*[@-~]|\u001b[@-Z\\-_])`, "g");
2388
2765
  const CONTROL_CHARACTER_RE = new RegExp(String.raw`[\u0000-\u001f\u007f-\u009f]+`, "g");
@@ -2395,7 +2772,6 @@ const DANGLING_TERMINAL_SEQUENCE_SUFFIX_RE = new RegExp(String.raw`(?:\u001b\][^
2395
2772
  async function handleCodexAppServerApprovalRequest(params) {
2396
2773
  const requestParams = isJsonObject(params.requestParams) ? params.requestParams : void 0;
2397
2774
  if (!matchesCurrentTurn(requestParams, params.threadId, params.turnId)) return;
2398
- if (!isSupportedAppServerApprovalMethod(params.method)) return unsupportedApprovalResponse();
2399
2775
  const context = buildApprovalContext({
2400
2776
  method: params.method,
2401
2777
  requestParams,
@@ -2436,7 +2812,20 @@ async function handleCodexAppServerApprovalRequest(params) {
2436
2812
  });
2437
2813
  return buildApprovalResponse(params.method, context.requestParams, policyOutcome.outcome);
2438
2814
  }
2439
- if (params.autoApprove === true) {
2815
+ const canAutoApproveConcreteToolCall = CONCRETE_TOOL_AUTO_APPROVAL_METHODS.has(params.method);
2816
+ if (canAutoApproveConcreteToolCall && params.autoApproveOpenClawToolPolicy === true && policyOutcome?.outcome === "allowed") {
2817
+ emitApprovalEvent(params.paramsForRun, {
2818
+ phase: "resolved",
2819
+ kind: context.kind,
2820
+ status: "approved",
2821
+ title: context.title,
2822
+ ...context.eventDetails,
2823
+ ...approvalEventScope(params.method, "approved-once"),
2824
+ message: "Codex app-server approval accepted by OpenClaw tool policy."
2825
+ });
2826
+ return buildApprovalResponse(params.method, context.requestParams, "approved-once");
2827
+ }
2828
+ if (canAutoApproveConcreteToolCall && params.autoApprove === true) {
2440
2829
  emitApprovalEvent(params.paramsForRun, {
2441
2830
  phase: "resolved",
2442
2831
  kind: context.kind,
@@ -2598,7 +2987,7 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
2598
2987
  ...nativeRelayOutcome.failureDisposition ? { failureDisposition: nativeRelayOutcome.failureDisposition } : {}
2599
2988
  };
2600
2989
  if (nativeRelayOutcome?.outcome === "approved-once" || nativeRelayOutcome?.outcome === "approved-session") return { outcome: nativeRelayOutcome.outcome };
2601
- if (nativeRelayOutcome?.handled) return { outcome: "no-decision" };
2990
+ if (nativeRelayOutcome?.handled) return { outcome: "allowed" };
2602
2991
  const hookChannelId = buildAgentHookContextChannelFields({
2603
2992
  sessionKey: params.paramsForRun.sessionKey,
2604
2993
  messageChannel: params.paramsForRun.messageChannel,
@@ -2606,6 +2995,7 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
2606
2995
  currentChannelId: params.paramsForRun.currentChannelId,
2607
2996
  messageTo: params.paramsForRun.messageTo
2608
2997
  }).channelId;
2998
+ const requester = buildCodexHookRequester(params.paramsForRun);
2609
2999
  const outcome = await runBeforeToolCallHook({
2610
3000
  toolName: policyRequest.toolName,
2611
3001
  params: policyRequest.params,
@@ -2620,7 +3010,14 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
2620
3010
  ...params.paramsForRun.sessionKey ? { sessionKey: params.paramsForRun.sessionKey } : {},
2621
3011
  ...params.paramsForRun.sessionId ? { sessionId: params.paramsForRun.sessionId } : {},
2622
3012
  ...params.paramsForRun.runId ? { runId: params.paramsForRun.runId } : {},
2623
- ...hookChannelId ? { channelId: hookChannelId } : {}
3013
+ ...hookChannelId ? { channelId: hookChannelId } : {},
3014
+ ...requester ? { requester } : {},
3015
+ trigger: params.paramsForRun.trigger,
3016
+ approvalReviewerDeviceId: params.paramsForRun.approvalReviewerDeviceId,
3017
+ turnSourceChannel: params.paramsForRun.messageChannel ?? params.paramsForRun.messageProvider,
3018
+ turnSourceTo: params.paramsForRun.currentMessagingTarget ?? params.paramsForRun.currentChannelId,
3019
+ turnSourceAccountId: params.paramsForRun.agentAccountId,
3020
+ turnSourceThreadId: params.paramsForRun.currentThreadTs
2624
3021
  }
2625
3022
  });
2626
3023
  if (outcome.blocked) return {
@@ -2633,6 +3030,7 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
2633
3030
  reason: "OpenClaw tool policy rewrote Codex app-server approval params; refusing original request."
2634
3031
  };
2635
3032
  if (outcome.approvalResolution) return { outcome: "approved-once" };
3033
+ return { outcome: "allowed" };
2636
3034
  }
2637
3035
  async function runNativeRelayToolPolicyForApprovalRequest(params) {
2638
3036
  if (params.method !== "item/commandExecution/requestApproval" || !params.nativeHookRelay?.allowedEvents.includes("pre_tool_use")) return;
@@ -3046,9 +3444,6 @@ function approvalKindForMethod(method) {
3046
3444
  if (method.includes("fileChange") || method.includes("Patch") || method.includes("permissions")) return "plugin";
3047
3445
  return "unknown";
3048
3446
  }
3049
- function isSupportedAppServerApprovalMethod(method) {
3050
- return method === "item/commandExecution/requestApproval" || method === "item/fileChange/requestApproval" || method === "item/permissions/requestApproval";
3051
- }
3052
3447
  function emitApprovalEvent(params, data) {
3053
3448
  params.onAgentEvent?.({
3054
3449
  stream: "approval",
@@ -3152,25 +3547,6 @@ function formatErrorMessage$1(error) {
3152
3547
  return error instanceof Error ? error.message : String(error);
3153
3548
  }
3154
3549
  //#endregion
3155
- //#region extensions/codex/src/app-server/agent-context-limits.ts
3156
- /** Resolves an agent override before falling back to the configured default. */
3157
- function resolveAgentContextLimitValue(params) {
3158
- const agents = asOptionalRecord(params.config?.agents);
3159
- const defaultValue = readPositiveInteger(asOptionalRecord(asOptionalRecord(agents?.defaults)?.contextLimits)?.[params.key]);
3160
- if (!params.agentId) return defaultValue;
3161
- const list = agents?.list;
3162
- if (!Array.isArray(list)) return defaultValue;
3163
- const normalizedAgentId = normalizeAgentId(params.agentId);
3164
- return readPositiveInteger(asOptionalRecord(asOptionalRecord(list.find((entry) => {
3165
- const entryId = asOptionalRecord(entry)?.id;
3166
- return typeof entryId === "string" && normalizeAgentId(entryId) === normalizedAgentId;
3167
- }))?.contextLimits)?.[params.key]) ?? defaultValue;
3168
- }
3169
- function readPositiveInteger(value) {
3170
- if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return;
3171
- return Math.floor(value);
3172
- }
3173
- //#endregion
3174
3550
  //#region extensions/codex/src/app-server/dynamic-tools.ts
3175
3551
  /**
3176
3552
  * Bridges OpenClaw runtime tools into Codex app-server dynamic tool specs and
@@ -3319,6 +3695,13 @@ const EXPLICIT_MESSAGE_REPLY_KEYS = [
3319
3695
  "replyToIdFull"
3320
3696
  ];
3321
3697
  const DEFAULT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS = 16e3;
3698
+ const LARGE_CONTEXT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS = 32e3;
3699
+ const XL_CONTEXT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS = 64e3;
3700
+ function resolveCodexDynamicToolResultMaxChars(contextWindowTokens) {
3701
+ if (typeof contextWindowTokens !== "number" || !Number.isFinite(contextWindowTokens) || contextWindowTokens <= 0) return DEFAULT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS;
3702
+ const tokens = Math.floor(contextWindowTokens);
3703
+ return Math.min(tokens >= 2e5 ? XL_CONTEXT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS : tokens >= 1e5 ? LARGE_CONTEXT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS : DEFAULT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS, Math.max(1, Math.floor(tokens * .3) * 4));
3704
+ }
3322
3705
  function computerFrameImageIdentity(content) {
3323
3706
  if (!Array.isArray(content)) return;
3324
3707
  const images = content.filter((block) => block.type === "image");
@@ -3337,7 +3720,7 @@ function invalidateComputerFrame(contextEpoch) {
3337
3720
  */
3338
3721
  function createCodexDynamicToolBridge(params) {
3339
3722
  const toolResultHookContext = toToolResultHookContext(params.hookContext);
3340
- const toolResultMaxChars = resolveCodexDynamicToolResultMaxChars(params.hookContext);
3723
+ const toolResultMaxChars = resolveCodexDynamicToolResultMaxChars(params.hookContext?.contextWindowTokens);
3341
3724
  const availableProjection = projectCodexDynamicTools(params.tools);
3342
3725
  const registeredProjection = params.registeredTools ? projectCodexDynamicTools(params.registeredTools) : availableProjection;
3343
3726
  const wrappedAvailableProjection = wrapProjectedCodexDynamicTools(availableProjection.tools, params.hookContext);
@@ -3376,6 +3759,7 @@ function createCodexDynamicToolBridge(params) {
3376
3759
  const legacyExtensionRunner = createCodexAppServerToolResultExtensionRunner(toolResultHookContext);
3377
3760
  const executionSnapshotStates = /* @__PURE__ */ new Map();
3378
3761
  const directToolNames = /* @__PURE__ */ new Set([...ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES, ...params.directToolNames ?? []]);
3762
+ let readRemoteWorkspaceFile;
3379
3763
  return {
3380
3764
  availableSpecs: createCodexDynamicToolSpecs({
3381
3765
  entries: availableTools,
@@ -3387,7 +3771,11 @@ function createCodexDynamicToolBridge(params) {
3387
3771
  loading: params.loading ?? "searchable",
3388
3772
  directToolNames
3389
3773
  }),
3774
+ resultContentSourceForTool: (toolName) => toolMap.get(toolName)?.tool.resultContentSource,
3390
3775
  telemetry,
3776
+ setRemoteWorkspaceFileReader: (reader) => {
3777
+ readRemoteWorkspaceFile = reader;
3778
+ },
3391
3779
  consumeToolExecutionSnapshot: (toolCallId) => {
3392
3780
  const state = executionSnapshotStates.get(toolCallId);
3393
3781
  executionSnapshotStates.delete(toolCallId);
@@ -3438,7 +3826,15 @@ function createCodexDynamicToolBridge(params) {
3438
3826
  };
3439
3827
  };
3440
3828
  try {
3441
- const preparedArgs = tool.prepareArguments ? tool.prepareArguments(args) : args;
3829
+ const toolArgs = tool.prepareArguments ? tool.prepareArguments(args) : args;
3830
+ const preparedArgs = toolName === "message" && isRecord(toolArgs) ? await prepareCodexRemoteWorkspaceMessageMedia({
3831
+ args: toolArgs,
3832
+ localWorkspaceRoot: params.hookContext?.workspaceDir,
3833
+ remoteWorkspaceRoot: params.hookContext?.remoteWorkspaceRoot,
3834
+ readRemoteFile: readRemoteWorkspaceFile,
3835
+ timeoutMs: params.hookContext?.remoteWorkspaceRequestTimeoutMs,
3836
+ signal
3837
+ }) : toolArgs;
3442
3838
  const telemetryArgs = isRecord(preparedArgs) ? preparedArgs : args;
3443
3839
  executedArgs = structuredClone(telemetryArgs);
3444
3840
  const messagingContext = {
@@ -3516,6 +3912,7 @@ function createCodexDynamicToolBridge(params) {
3516
3912
  contentItems,
3517
3913
  success: !resultIsError
3518
3914
  }, terminalType);
3915
+ withDynamicToolTranscriptDetails(response, result.details);
3519
3916
  withDiagnosticFailureDisposition(response, resultFailureKind);
3520
3917
  const blocksSourceReplyTermination = hasExplicitNonSourceMessageRoute(executedArgs, params.hookContext, confirmedMessagingTarget);
3521
3918
  const deliveredSourceReply = isDeliveredMessageToolOnlySourceReplyResult({
@@ -3669,7 +4066,8 @@ function createCodexDynamicToolSpecs(params) {
3669
4066
  const specs = [];
3670
4067
  const namespaceTools = [];
3671
4068
  const directOnlyNamespaceTools = [];
3672
- for (const entry of params.entries) {
4069
+ const entries = params.loading === "direct" ? params.entries : params.entries.toSorted((left, right) => left.name.localeCompare(right.name));
4070
+ for (const entry of entries) {
3673
4071
  const functionSpec = createCodexDynamicToolFunctionSpec({ entry });
3674
4072
  if (entry.name === "openclaw" && params.directToolNames.has(entry.name)) {
3675
4073
  specs.push(functionSpec);
@@ -3856,13 +4254,6 @@ function toToolResultHookContext(ctx) {
3856
4254
  ...channelId && { channelId }
3857
4255
  };
3858
4256
  }
3859
- function resolveCodexDynamicToolResultMaxChars(ctx) {
3860
- return resolveAgentContextLimitValue({
3861
- config: ctx?.config,
3862
- agentId: ctx?.agentId,
3863
- key: "toolResultMaxChars"
3864
- }) ?? DEFAULT_CODEX_DYNAMIC_TOOL_RESULT_MAX_CHARS;
3865
- }
3866
4257
  function composeAbortSignals(...signals) {
3867
4258
  const activeSignals = signals.filter((signal) => Boolean(signal));
3868
4259
  if (activeSignals.length === 0) return new AbortController().signal;
@@ -4126,4 +4517,4 @@ function isCronAddAction(args) {
4126
4517
  return typeof action === "string" && action.trim().toLowerCase() === "add";
4127
4518
  }
4128
4519
  //#endregion
4129
- export { shouldSynthesizeToolProgressForItem as $, resolveCodexNativeHookRelayEvents as A, shouldReleaseTurnAfterTerminalDynamicTool as B, CodexNativeToolLifecycleProjector as C, buildCodexNativeHookRelayDisabledConfig as D, buildCodexNativeHookRelayConfig as E, isDynamicToolTerminalDiagnosticEvent as F, isNonSuccessItemStatus as G, toCodexDynamicToolProtocolResponse as H, isMatchingDynamicToolTerminalDiagnostic as I, itemName as J, isSideEffectingNativeToolItem as K, resolveDynamicToolCallTimeoutMs as L, scheduleCodexNativeHookRelayUnregister as M, handleDynamicToolCallWithTimeout as N, createCodexNativeHookRelay as O, hasPendingDynamicToolTerminalDiagnostic as P, shouldRecordNativeToolTranscript as Q, resolveTerminalDynamicToolBatchAction as R, filterToolsForVisionInputs as S, CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS as T, resolveCodexToolAbortTerminalReason as U, toCodexDynamicToolProgressResponse as V, isMutatingNativeToolItem as W, itemTitle as X, itemStatus as Y, shouldClearTerminalPresentationForNativeItem as Z, resolveCodexMessageToolProvider as _, emitDynamicToolStartedDiagnostic as a, shouldRequireCodexSandboxExecServerEnvironment as b, resolveCodexProviderWebSearchSupportForClient as c, createCodexDynamicToolBuildStageTracker as d, unknownItemStatus as et, disableCodexPluginThreadConfig as f, resolveCodexExternalSandboxPolicyForOpenClawSandbox as g, resolveCodexAppServerHookChannelId as h, emitDynamicToolErrorDiagnostic as i, resolveCodexNativeHookRelayTtlMs as j, emitCodexNativePreToolUseFailureDiagnostic as k, settleCodexSourceReplyFinality as l, resolveCodexAppServerExecutionCwd as m, handleCodexAppServerApprovalRequest as n, emitDynamicToolTerminalDiagnostic as o, formatCodexDynamicToolBuildStageSummary as p, itemKind as q, handleCodexAppServerElicitationRequest as r, resolveCodexProviderWebSearchSupport as s, createCodexDynamicToolBridge as t, buildDynamicTools as u, resolveCodexSandboxEnvironmentSelection as v, CODEX_NATIVE_HOOK_RELAY_EVENTS as w, shouldWarnCodexDynamicToolBuildStageSummary as x, shouldEnableCodexAppServerNativeToolSurface as y, shouldBlockTerminalReleaseForNonTerminalDynamicToolResult as z };
4520
+ export { shouldClearTerminalPresentationForNativeItem as $, createCodexNativeHookRelay as A, resolveTerminalDynamicToolBatchAction as B, filterToolsForVisionInputs as C, CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS as D, CODEX_NATIVE_HOOK_RELAY_EVENTS as E, handleDynamicToolCallWithTimeout as F, resolveCodexToolAbortTerminalReason as G, shouldReleaseTurnAfterTerminalDynamicTool as H, hasPendingDynamicToolTerminalDiagnostic as I, isSideEffectingNativeToolItem as J, isMutatingNativeToolItem as K, isDynamicToolTerminalDiagnosticEvent as L, resolveCodexNativeHookRelayEvents as M, resolveCodexNativeHookRelayTtlMs as N, buildCodexNativeHookRelayConfig as O, scheduleCodexNativeHookRelayUnregister as P, itemTitle as Q, isMatchingDynamicToolTerminalDiagnostic as R, shouldWarnCodexDynamicToolBuildStageSummary as S, CodexNativeToolLifecycleProjector as T, toCodexDynamicToolProgressResponse as U, shouldBlockTerminalReleaseForNonTerminalDynamicToolResult as V, toCodexDynamicToolProtocolResponse as W, itemName as X, itemKind as Y, itemStatus as Z, resolveCodexExternalSandboxPolicyForOpenClawSandbox as _, emitDynamicToolStartedDiagnostic as a, shouldEnableCodexAppServerNativeToolSurface as b, resolveCodexProviderWebSearchSupportForClient as c, buildDynamicTools as d, shouldRecordNativeToolTranscript as et, createCodexDynamicToolBuildStageTracker as f, resolveCodexAppServerHookChannelId as g, resolveCodexAppServerExecutionCwd as h, emitDynamicToolErrorDiagnostic as i, emitCodexNativePreToolUseFailureDiagnostic as j, buildCodexNativeHookRelayDisabledConfig as k, buildCodexHookRequester as l, formatCodexDynamicToolBuildStageSummary as m, handleCodexAppServerApprovalRequest as n, unknownItemStatus as nt, emitDynamicToolTerminalDiagnostic as o, disableCodexPluginThreadConfig as p, isNonSuccessItemStatus as q, handleCodexAppServerElicitationRequest as r, resolveCodexProviderWebSearchSupport as s, createCodexDynamicToolBridge as t, shouldSynthesizeToolProgressForItem as tt, settleCodexSourceReplyFinality as u, resolveCodexMessageToolProvider as v, readBoundedCodexRemoteWorkspaceFile as w, shouldRequireCodexSandboxExecServerEnvironment as x, resolveCodexSandboxEnvironmentSelection as y, resolveDynamicToolCallTimeoutMs as z };