@openclaw/codex 2026.7.2-beta.5 → 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.
- package/dist/{app-server-policy-BmDgseo_.js → app-server-policy-BSaO2JIT.js} +1 -1
- package/dist/{attempt-notifications-CMR2_fmR.js → attempt-notifications-AP-a-Q5k.js} +1 -1
- package/dist/{bounded-turn-FEbUXoYT.js → bounded-turn-D2ZAgZVz.js} +29 -22
- package/dist/{capabilities-BHRSI6Cn.js → capabilities-BFDTVYr4.js} +1 -1
- package/dist/cli-metadata.js +1 -1
- package/dist/{command-formatters-ico4ZFnk.js → command-formatters-C336eRVS.js} +2 -2
- package/dist/{command-handlers-CRmnwI2N.js → command-handlers-DAHUSry9.js} +12 -12
- package/dist/{compact-dEN49UH6.js → compact-D_uvHurS.js} +5 -5
- package/dist/{computer-use-BjGylO-E.js → computer-use-CYEc8U3w.js} +111 -113
- package/dist/{conversation-control-DE7d9DOj.js → conversation-control-xsIeXbo4.js} +6 -6
- package/dist/doctor-contract-api.js +1 -1
- package/dist/{dynamic-tools-LIt9ojep.js → dynamic-tools-D2gYkSBO.js} +438 -51
- package/dist/effective-mcp-catalog-Bbd98Inw.js +114 -0
- package/dist/{event-projector-usage-tvgb1NCX.js → event-projector-usage-Xp4AlR2k.js} +20 -3
- package/dist/harness.js +13 -9
- package/dist/index.js +131 -108
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{models-BA01NEez.js → models-BBMKU4jA.js} +2 -2
- package/dist/{notification-correlation-Bj_Ak_jI.js → notification-correlation-CU6_AnOb.js} +3 -3
- package/dist/{plugin-app-cache-key-DSXX-rXO.js → plugin-app-cache-key-DztPafOA.js} +132 -39
- package/dist/{rate-limits-S_PXs0ul.js → rate-limits-CU-gVCCS.js} +1 -1
- package/dist/{request-CXpPennK.js → request-qKh331yu.js} +3 -3
- package/dist/{run-attempt-C5GTPJOE.js → run-attempt-CY9WlDLc.js} +677 -199
- package/dist/{runtime-artifact-C2ITXdhM.js → runtime-artifact-CmtG_t1L.js} +90 -24
- package/dist/{sandbox-guard-D1Wky__S.js → sandbox-guard-NuhpXmPB.js} +4 -0
- package/dist/{session-binding-DlVQ-Pxs.js → session-binding-BXpo9ymA.js} +60 -7
- package/dist/{session-catalog-CP2oCcEb.js → session-catalog-DyOgR4WH.js} +268 -44
- package/dist/{session-cli-CqZ0Fp2a.js → session-cli-2PZaFLRh.js} +1 -1
- package/dist/{session-upstream-marker-QE-m5QFG.js → session-upstream-marker-B6U7tLw5.js} +1 -1
- package/dist/{settled-turn-finalizer-CA_2QXIE.js → settled-turn-finalizer-CXY2Z9dT.js} +19 -6
- package/dist/{shared-client-BVKE40c-.js → shared-client-DTbvH-XV.js} +166 -49
- package/dist/{side-question-CO12sHxo.js → side-question-_ulBk1AL.js} +10 -10
- package/dist/{thread-lifecycle-DRg_FdzC.js → thread-lifecycle-DF53ogiA.js} +968 -214
- package/dist/{protocol-validators-Dm4eXZmt.js → transcript-mirror-DzsdNCf5.js} +589 -2
- package/dist/{upstream-session-fork-_Vrz4WJr.js → upstream-session-fork-Dy4Xvuoj.js} +3 -3
- package/dist/{usage-UpVbdX5G.js → usage-CQ6GhD5-.js} +3 -3
- package/dist/{web-search-provider.runtime-CaQMYDbp.js → web-search-provider.runtime-CCFhg2z5.js} +3 -3
- package/package.json +6 -7
- package/dist/attempt-client-cleanup-Ia52yRWc.js +0 -127
- package/dist/realtime-voice-api-BPcA1uN9.js +0 -494
- package/dist/realtime-voice-api.js +0 -2
- package/dist/transcript-mirror-uKA-A4tB.js +0 -550
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { D as isForcedPrivateQaCodexRuntime, E as filterCodexDynamicToolsWithOpenClawShell, O as isSystemAgentOnlyCodexDynamicToolAllowlist, T as filterCodexDynamicTools,
|
|
4
|
-
import {
|
|
5
|
-
import { r as formatCodexDisplayText } from "./command-formatters-
|
|
6
|
-
import { a as resolveCodexNodeExecToolOverrides, i as resolveCodexNativeExecutionPolicy } from "./sandbox-guard-
|
|
7
|
-
import { n as readCodexNotificationThreadId, r as readCodexNotificationTurnId } from "./notification-correlation-
|
|
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
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";
|
|
10
11
|
import { createHash } from "node:crypto";
|
|
@@ -14,6 +15,9 @@ import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-util
|
|
|
14
15
|
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
15
16
|
import { emitTrustedDiagnosticEvent, hasPendingInternalDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
16
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";
|
|
17
21
|
import { isApprovalNotFoundError } from "openclaw/plugin-sdk/error-runtime";
|
|
18
22
|
import { normalizeOpenAIToolSchemas } from "openclaw/plugin-sdk/provider-tools";
|
|
19
23
|
//#region extensions/codex/src/app-server/event-projector-items.ts
|
|
@@ -108,7 +112,7 @@ function shouldSynthesizeToolProgressForItem(item) {
|
|
|
108
112
|
}
|
|
109
113
|
}
|
|
110
114
|
function shouldRecordNativeToolTranscript(item) {
|
|
111
|
-
return shouldSynthesizeToolProgressForItem(item)
|
|
115
|
+
return shouldSynthesizeToolProgressForItem(item);
|
|
112
116
|
}
|
|
113
117
|
function isMutatingNativeToolItem(item) {
|
|
114
118
|
if (item.type === "commandExecution") return true;
|
|
@@ -604,6 +608,19 @@ const CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS = 1e3;
|
|
|
604
608
|
const CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC = 10;
|
|
605
609
|
const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS = 1e4;
|
|
606
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
|
+
};
|
|
607
624
|
/** Defers relay unregister so late native hook subprocesses can still resolve. */
|
|
608
625
|
function scheduleCodexNativeHookRelayUnregister(params) {
|
|
609
626
|
let pending;
|
|
@@ -662,6 +679,7 @@ function createCodexNativeHookRelay(params) {
|
|
|
662
679
|
runId: params.runId,
|
|
663
680
|
...params.channelId ? { channelId: params.channelId } : {},
|
|
664
681
|
...params.requester ? { requester: params.requester } : {},
|
|
682
|
+
...params.approvalContext ? { approvalContext: params.approvalContext } : {},
|
|
665
683
|
allowedEvents: params.events,
|
|
666
684
|
preToolUseLoopDetection: params.loopDetectionPreToolUseRelay,
|
|
667
685
|
ttlMs: resolveCodexNativeHookRelayTtlMs({
|
|
@@ -730,18 +748,23 @@ function buildCodexNativeHookRelayConfig(params) {
|
|
|
730
748
|
}
|
|
731
749
|
const timeout = normalizeHookTimeoutSec(params.hookTimeoutSec);
|
|
732
750
|
const command = params.relay.commandForEvent(event, { timeoutMs: resolveCodexNativeHookRelayCommandTimeoutMs(timeout) });
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
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
|
+
}];
|
|
740
762
|
const state = {
|
|
741
763
|
enabled: true,
|
|
742
764
|
trusted_hash: codexCommandHookTrustedHash({
|
|
743
765
|
event,
|
|
744
766
|
command,
|
|
767
|
+
matcher,
|
|
745
768
|
timeout,
|
|
746
769
|
statusMessage: "OpenClaw native hook relay"
|
|
747
770
|
})
|
|
@@ -769,9 +792,26 @@ function resolveCodexNativeHookRelayCommandTimeoutMs(hookTimeoutSec) {
|
|
|
769
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)));
|
|
770
793
|
return Math.max(1, parentTimeoutMs - parentMarginMs);
|
|
771
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
|
+
}
|
|
772
811
|
function codexCommandHookTrustedHash(params) {
|
|
773
812
|
const identity = {
|
|
774
813
|
event_name: CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[params.event],
|
|
814
|
+
...params.matcher ? { matcher: params.matcher } : {},
|
|
775
815
|
hooks: [{
|
|
776
816
|
async: false,
|
|
777
817
|
command: params.command,
|
|
@@ -984,6 +1024,322 @@ var CodexNativeToolLifecycleProjector = class {
|
|
|
984
1024
|
}
|
|
985
1025
|
};
|
|
986
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
|
|
987
1343
|
//#region extensions/codex/src/app-server/dynamic-tool-build-state.ts
|
|
988
1344
|
/** Mutable dependency seam shared by dynamic-tool construction and its behavioral tests. */
|
|
989
1345
|
const dynamicToolBuildState = {};
|
|
@@ -1193,8 +1549,10 @@ async function buildDynamicTools(input) {
|
|
|
1193
1549
|
const toolBuildStages = createCodexDynamicToolBuildStageTracker({ enabled: input.profilerEnabled });
|
|
1194
1550
|
const modelHasVision = params.model.input?.includes("image") ?? false;
|
|
1195
1551
|
const agentDir = params.agentDir ?? resolveAgentDir$1(params.config ?? {}, input.sessionAgentId);
|
|
1196
|
-
const
|
|
1197
|
-
|
|
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;
|
|
1198
1556
|
toolBuildStages.mark("load-agent-harness-tools");
|
|
1199
1557
|
const sessionKeys = resolveOpenClawCodingToolsSessionKeys(params, input.sandboxSessionKey);
|
|
1200
1558
|
const nativeExecutionPolicy = resolveCodexNativeExecutionPolicyForDynamicTools(input);
|
|
@@ -1305,7 +1663,8 @@ async function buildDynamicTools(input) {
|
|
|
1305
1663
|
});
|
|
1306
1664
|
toolBuildStages.mark("vision-filtering");
|
|
1307
1665
|
const webSearchPresent = visionFilteredTools.some((tool) => tool.name === "web_search");
|
|
1308
|
-
const
|
|
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({
|
|
1309
1668
|
config: params.config,
|
|
1310
1669
|
modelProvider: params.model.provider,
|
|
1311
1670
|
modelId: params.modelId,
|
|
@@ -1325,9 +1684,11 @@ async function buildDynamicTools(input) {
|
|
|
1325
1684
|
inputProvenance: params.inputProvenance,
|
|
1326
1685
|
trustedInternalHandoff: params.trustedInternalHandoff,
|
|
1327
1686
|
scheduledToolPolicy: params.scheduledToolPolicy
|
|
1328
|
-
})
|
|
1687
|
+
}) : {
|
|
1688
|
+
allowed: false,
|
|
1689
|
+
persistentAllowed: false
|
|
1690
|
+
};
|
|
1329
1691
|
const transientWebSearchRestriction = !webSearchPolicy.allowed && webSearchPolicy.persistentAllowed || isCodexMemoryFlushRun(params);
|
|
1330
|
-
const persistentCodexWebSearchSurface = params.config?.tools?.web?.search?.enabled !== false && !(input.pluginConfig.codexDynamicToolsExclude ?? []).some((name) => normalizeCodexDynamicToolName(name) === "web_search");
|
|
1331
1692
|
input.onPersistentWebSearchPolicyResolved?.(webSearchPresent || persistentCodexWebSearchSurface && transientWebSearchRestriction && webSearchPolicy.persistentAllowed);
|
|
1332
1693
|
const filteredTools = filterCodexDynamicToolsForAllowlist(visionFilteredTools, includeForcedCodexDynamicToolAllow(params.toolsAllow, messagePolicyParams));
|
|
1333
1694
|
toolBuildStages.mark("allowlist-filter");
|
|
@@ -1440,27 +1801,6 @@ function resolveCodexAppServerExecutionCwd(params) {
|
|
|
1440
1801
|
remoteWorkspaceRoot: params.remoteWorkspaceRoot
|
|
1441
1802
|
});
|
|
1442
1803
|
}
|
|
1443
|
-
/** Projects a local OpenClaw workspace cwd into the remote Codex app-server workspace root. */
|
|
1444
|
-
function mapCodexAppServerRemoteWorkspacePath(params) {
|
|
1445
|
-
if (!params.remoteWorkspaceRoot) return params.value;
|
|
1446
|
-
const localRoot = normalizeRemoteWorkspaceMatchPath(params.localWorkspaceRoot);
|
|
1447
|
-
const remoteRoot = normalizeRemoteWorkspaceMatchPath(params.remoteWorkspaceRoot);
|
|
1448
|
-
const normalizedValue = normalizeRemoteWorkspaceMatchPath(params.value);
|
|
1449
|
-
if (!localRoot || !remoteRoot) throw new Error("Codex remoteWorkspaceRoot requires non-empty workspace roots.");
|
|
1450
|
-
if (normalizedValue === localRoot) return remoteRoot;
|
|
1451
|
-
const prefix = `${localRoot}/`;
|
|
1452
|
-
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.`);
|
|
1453
|
-
return joinRemoteWorkspacePath(remoteRoot, normalizedValue.slice(prefix.length));
|
|
1454
|
-
}
|
|
1455
|
-
function normalizeRemoteWorkspaceMatchPath(value) {
|
|
1456
|
-
return trimTrailingPathSeparator(value.replace(/\\/gu, "/"));
|
|
1457
|
-
}
|
|
1458
|
-
function trimTrailingPathSeparator(value) {
|
|
1459
|
-
return value.length > 1 ? value.replace(/[\\/]+$/u, "") : value;
|
|
1460
|
-
}
|
|
1461
|
-
function joinRemoteWorkspacePath(remoteRoot, suffix) {
|
|
1462
|
-
return remoteRoot === "/" ? `/${suffix}` : `${remoteRoot}/${suffix}`;
|
|
1463
|
-
}
|
|
1464
1804
|
/** Converts OpenClaw sandbox networking into Codex's external-sandbox policy shape. */
|
|
1465
1805
|
function resolveCodexExternalSandboxPolicyForOpenClawSandbox(sandbox) {
|
|
1466
1806
|
return {
|
|
@@ -1602,6 +1942,20 @@ function settleCodexSourceReplyFinality(owner, turnSucceeded) {
|
|
|
1602
1942
|
return turnSucceeded && intents.some((intent) => intent.record.sourceReplyFinal === true);
|
|
1603
1943
|
}
|
|
1604
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
|
|
1605
1959
|
//#region extensions/codex/src/app-server/provider-capabilities.ts
|
|
1606
1960
|
function resolveOverriddenProviderWebSearchSupport(modelProviderOverride) {
|
|
1607
1961
|
const provider = modelProviderOverride?.trim().toLowerCase();
|
|
@@ -2405,6 +2759,7 @@ const PERMISSION_VALUE_MAX_LENGTH = 48;
|
|
|
2405
2759
|
const COMMAND_PREVIEW_WITH_DETAILS_MAX_LENGTH = 80;
|
|
2406
2760
|
const APPROVAL_PREVIEW_SCAN_MAX_LENGTH = 4096;
|
|
2407
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"]);
|
|
2408
2763
|
const ANSI_OSC_SEQUENCE_RE = new RegExp(String.raw`(?:\u001b]|\u009d)[^\u001b\u009c\u0007]*(?:\u0007|\u001b\\|\u009c)`, "g");
|
|
2409
2764
|
const ANSI_CONTROL_SEQUENCE_RE = new RegExp(String.raw`(?:\u001b\[[0-?]*[ -/]*[@-~]|\u009b[0-?]*[ -/]*[@-~]|\u001b[@-Z\\-_])`, "g");
|
|
2410
2765
|
const CONTROL_CHARACTER_RE = new RegExp(String.raw`[\u0000-\u001f\u007f-\u009f]+`, "g");
|
|
@@ -2417,7 +2772,6 @@ const DANGLING_TERMINAL_SEQUENCE_SUFFIX_RE = new RegExp(String.raw`(?:\u001b\][^
|
|
|
2417
2772
|
async function handleCodexAppServerApprovalRequest(params) {
|
|
2418
2773
|
const requestParams = isJsonObject(params.requestParams) ? params.requestParams : void 0;
|
|
2419
2774
|
if (!matchesCurrentTurn(requestParams, params.threadId, params.turnId)) return;
|
|
2420
|
-
if (!isSupportedAppServerApprovalMethod(params.method)) return unsupportedApprovalResponse();
|
|
2421
2775
|
const context = buildApprovalContext({
|
|
2422
2776
|
method: params.method,
|
|
2423
2777
|
requestParams,
|
|
@@ -2458,7 +2812,20 @@ async function handleCodexAppServerApprovalRequest(params) {
|
|
|
2458
2812
|
});
|
|
2459
2813
|
return buildApprovalResponse(params.method, context.requestParams, policyOutcome.outcome);
|
|
2460
2814
|
}
|
|
2461
|
-
|
|
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) {
|
|
2462
2829
|
emitApprovalEvent(params.paramsForRun, {
|
|
2463
2830
|
phase: "resolved",
|
|
2464
2831
|
kind: context.kind,
|
|
@@ -2620,7 +2987,7 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
|
|
|
2620
2987
|
...nativeRelayOutcome.failureDisposition ? { failureDisposition: nativeRelayOutcome.failureDisposition } : {}
|
|
2621
2988
|
};
|
|
2622
2989
|
if (nativeRelayOutcome?.outcome === "approved-once" || nativeRelayOutcome?.outcome === "approved-session") return { outcome: nativeRelayOutcome.outcome };
|
|
2623
|
-
if (nativeRelayOutcome?.handled) return { outcome: "
|
|
2990
|
+
if (nativeRelayOutcome?.handled) return { outcome: "allowed" };
|
|
2624
2991
|
const hookChannelId = buildAgentHookContextChannelFields({
|
|
2625
2992
|
sessionKey: params.paramsForRun.sessionKey,
|
|
2626
2993
|
messageChannel: params.paramsForRun.messageChannel,
|
|
@@ -2628,6 +2995,7 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
|
|
|
2628
2995
|
currentChannelId: params.paramsForRun.currentChannelId,
|
|
2629
2996
|
messageTo: params.paramsForRun.messageTo
|
|
2630
2997
|
}).channelId;
|
|
2998
|
+
const requester = buildCodexHookRequester(params.paramsForRun);
|
|
2631
2999
|
const outcome = await runBeforeToolCallHook({
|
|
2632
3000
|
toolName: policyRequest.toolName,
|
|
2633
3001
|
params: policyRequest.params,
|
|
@@ -2642,7 +3010,14 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
|
|
|
2642
3010
|
...params.paramsForRun.sessionKey ? { sessionKey: params.paramsForRun.sessionKey } : {},
|
|
2643
3011
|
...params.paramsForRun.sessionId ? { sessionId: params.paramsForRun.sessionId } : {},
|
|
2644
3012
|
...params.paramsForRun.runId ? { runId: params.paramsForRun.runId } : {},
|
|
2645
|
-
...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
|
|
2646
3021
|
}
|
|
2647
3022
|
});
|
|
2648
3023
|
if (outcome.blocked) return {
|
|
@@ -2655,6 +3030,7 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
|
|
|
2655
3030
|
reason: "OpenClaw tool policy rewrote Codex app-server approval params; refusing original request."
|
|
2656
3031
|
};
|
|
2657
3032
|
if (outcome.approvalResolution) return { outcome: "approved-once" };
|
|
3033
|
+
return { outcome: "allowed" };
|
|
2658
3034
|
}
|
|
2659
3035
|
async function runNativeRelayToolPolicyForApprovalRequest(params) {
|
|
2660
3036
|
if (params.method !== "item/commandExecution/requestApproval" || !params.nativeHookRelay?.allowedEvents.includes("pre_tool_use")) return;
|
|
@@ -3068,9 +3444,6 @@ function approvalKindForMethod(method) {
|
|
|
3068
3444
|
if (method.includes("fileChange") || method.includes("Patch") || method.includes("permissions")) return "plugin";
|
|
3069
3445
|
return "unknown";
|
|
3070
3446
|
}
|
|
3071
|
-
function isSupportedAppServerApprovalMethod(method) {
|
|
3072
|
-
return method === "item/commandExecution/requestApproval" || method === "item/fileChange/requestApproval" || method === "item/permissions/requestApproval";
|
|
3073
|
-
}
|
|
3074
3447
|
function emitApprovalEvent(params, data) {
|
|
3075
3448
|
params.onAgentEvent?.({
|
|
3076
3449
|
stream: "approval",
|
|
@@ -3386,6 +3759,7 @@ function createCodexDynamicToolBridge(params) {
|
|
|
3386
3759
|
const legacyExtensionRunner = createCodexAppServerToolResultExtensionRunner(toolResultHookContext);
|
|
3387
3760
|
const executionSnapshotStates = /* @__PURE__ */ new Map();
|
|
3388
3761
|
const directToolNames = /* @__PURE__ */ new Set([...ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES, ...params.directToolNames ?? []]);
|
|
3762
|
+
let readRemoteWorkspaceFile;
|
|
3389
3763
|
return {
|
|
3390
3764
|
availableSpecs: createCodexDynamicToolSpecs({
|
|
3391
3765
|
entries: availableTools,
|
|
@@ -3397,7 +3771,11 @@ function createCodexDynamicToolBridge(params) {
|
|
|
3397
3771
|
loading: params.loading ?? "searchable",
|
|
3398
3772
|
directToolNames
|
|
3399
3773
|
}),
|
|
3774
|
+
resultContentSourceForTool: (toolName) => toolMap.get(toolName)?.tool.resultContentSource,
|
|
3400
3775
|
telemetry,
|
|
3776
|
+
setRemoteWorkspaceFileReader: (reader) => {
|
|
3777
|
+
readRemoteWorkspaceFile = reader;
|
|
3778
|
+
},
|
|
3401
3779
|
consumeToolExecutionSnapshot: (toolCallId) => {
|
|
3402
3780
|
const state = executionSnapshotStates.get(toolCallId);
|
|
3403
3781
|
executionSnapshotStates.delete(toolCallId);
|
|
@@ -3448,7 +3826,15 @@ function createCodexDynamicToolBridge(params) {
|
|
|
3448
3826
|
};
|
|
3449
3827
|
};
|
|
3450
3828
|
try {
|
|
3451
|
-
const
|
|
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;
|
|
3452
3838
|
const telemetryArgs = isRecord(preparedArgs) ? preparedArgs : args;
|
|
3453
3839
|
executedArgs = structuredClone(telemetryArgs);
|
|
3454
3840
|
const messagingContext = {
|
|
@@ -3680,7 +4066,8 @@ function createCodexDynamicToolSpecs(params) {
|
|
|
3680
4066
|
const specs = [];
|
|
3681
4067
|
const namespaceTools = [];
|
|
3682
4068
|
const directOnlyNamespaceTools = [];
|
|
3683
|
-
|
|
4069
|
+
const entries = params.loading === "direct" ? params.entries : params.entries.toSorted((left, right) => left.name.localeCompare(right.name));
|
|
4070
|
+
for (const entry of entries) {
|
|
3684
4071
|
const functionSpec = createCodexDynamicToolFunctionSpec({ entry });
|
|
3685
4072
|
if (entry.name === "openclaw" && params.directToolNames.has(entry.name)) {
|
|
3686
4073
|
specs.push(functionSpec);
|
|
@@ -4130,4 +4517,4 @@ function isCronAddAction(args) {
|
|
|
4130
4517
|
return typeof action === "string" && action.trim().toLowerCase() === "add";
|
|
4131
4518
|
}
|
|
4132
4519
|
//#endregion
|
|
4133
|
-
export {
|
|
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 };
|