@openclaw/codex 2026.8.1-beta.3 → 2026.9.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/api.js +3 -3
  2. package/dist/{attempt-client-cleanup-URjUlCHg.js → attempt-client-cleanup-bfsmcQ6m.js} +21 -2
  3. package/dist/{binding-connection-DAvZwp96.js → binding-connection-BMvPX73o.js} +2 -2
  4. package/dist/{bounded-turn-4BjrZEwW.js → bounded-turn-B7iBUcuP.js} +8 -8
  5. package/dist/{capabilities-CH3K57v4.js → capabilities-BRMh0aIq.js} +1 -1
  6. package/dist/cli-metadata.js +1 -1
  7. package/dist/{command-formatters-D0nZ3qtd.js → command-formatters-DEsIufkt.js} +2 -2
  8. package/dist/{command-handlers-DC86lgT4.js → command-handlers-D_-1_qRp.js} +43 -32
  9. package/dist/{command-rpc-xUIIonsO.js → command-rpc-CCeCkf_v.js} +4 -4
  10. package/dist/{compact-COqgBRny.js → compact-BglNFZrQ.js} +8 -8
  11. package/dist/{config-CpNeo5AE.js → config-giW8X0QA.js} +82 -28
  12. package/dist/doctor-contract-api.js +9 -8
  13. package/dist/{dynamic-tools-fJZhl4CE.js → dynamic-tools-Dnlfa--w.js} +108 -820
  14. package/dist/{effective-mcp-catalog-4YKn51aG.js → effective-mcp-catalog-CTeGrnfO.js} +2 -2
  15. package/dist/{event-projector-assistant-message-BwxV4d6N.js → event-projector-assistant-message-C5-Ds8mA.js} +18 -1
  16. package/dist/{event-projector-usage-CLwNEJqW.js → event-projector-usage-Bk1I90Bg.js} +3 -3
  17. package/dist/{harness-DoKQcF8n.js → harness-Csz0F-DK.js} +11 -11
  18. package/dist/harness.js +1 -1
  19. package/dist/index.js +130 -172
  20. package/dist/{isolated-completion-DPMtlH9r.js → isolated-completion-Cv6FqygM.js} +4 -4
  21. package/dist/{managed-binary-By4FZC6i.js → managed-binary-DFYK4DWq.js} +43 -7
  22. package/dist/media-understanding-provider.js +1 -1
  23. package/dist/{model-catalog-CmGoqVKB.js → model-catalog-i9rNBthX.js} +4 -4
  24. package/dist/{models-CZw1wClT.js → models-TS3HBU-q.js} +4 -3
  25. package/dist/{native-subagent-monitor-GGq3RnOo.js → native-subagent-monitor-4Mfq2HDs.js} +176 -19
  26. package/dist/{node-exec-server.runtime-Bsu1LdFZ.js → node-exec-server.runtime-BpWqJO1V.js} +2 -2
  27. package/dist/{plugin-app-cache-key-DMNQ8Zi8.js → plugin-app-cache-key-C1CAdfcP.js} +6 -3
  28. package/dist/{plugin-approval-roundtrip-C75Yiiwc.js → plugin-approval-roundtrip-bUm777tR.js} +1 -1
  29. package/dist/{plugin-marketplace-discovery-CSWDkMrP.js → plugin-marketplace-discovery-B9DvaAG-.js} +29 -38
  30. package/dist/{plugin-metadata-cache-BXH5NF-4.js → plugin-metadata-cache-CTCT3w5L.js} +162 -135
  31. package/dist/{protocol-validators-VAVyEnDp.js → protocol-validators-VeJxxJ0U.js} +25 -3
  32. package/dist/{rate-limits-BI1dMHDG.js → rate-limits-C5_D8Gmb.js} +1 -1
  33. package/dist/{request-DSK0W6YI.js → request-DXXGHko0.js} +2 -2
  34. package/dist/{run-attempt-BV3HzuCf.js → run-attempt-DYc0QCBF.js} +346 -267
  35. package/dist/{sandbox-guard-C1qEeZ5C.js → sandbox-guard-kuWTn9C5.js} +1 -1
  36. package/dist/{session-binding-BTrLf9Qm.js → session-binding-Bcynfd-_.js} +1 -1
  37. package/dist/{session-catalog-ezP_UcRM.js → session-catalog-DaLKVtpH.js} +17 -16
  38. package/dist/{session-cli-B1hKvfkI.js → session-cli-DMc4oS2w.js} +1 -1
  39. package/dist/session-permission-policy-BrNH1bjy.js +160 -0
  40. package/dist/{session-retirement-CE4iMiLh.js → session-retirement-CcNC3YSY.js} +3 -3
  41. package/dist/{session-upstream-marker-D_2npHlA.js → session-upstream-marker-BIkusb95.js} +1 -1
  42. package/dist/{settled-turn-finalizer-Dx1onIqg.js → settled-turn-finalizer-Dc3ApjU4.js} +4 -4
  43. package/dist/{shared-client-Dw-rzii0.js → shared-client-DKz23hHY.js} +1026 -248
  44. package/dist/{side-question-DbXkJZzg.js → side-question-DjA_jsNe.js} +65 -36
  45. package/dist/{thread-lifecycle-DKdrnTMX.js → thread-lifecycle-L7oEkTlq.js} +850 -35
  46. package/dist/{thread-ownership-i2NBwDUL.js → thread-ownership-DZeuKH1j.js} +2 -2
  47. package/dist/{thread-resume-CdJk2Ehf.js → thread-resume-BE9qNEW0.js} +3 -3
  48. package/dist/{transcript-mirror-BU0_LKAL.js → transcript-mirror-DsCMWBca.js} +91 -10
  49. package/dist/{upstream-session-fork-BDVJCLAD.js → upstream-session-fork-DlaHxSLC.js} +3 -3
  50. package/dist/{usage-9dKJCgcy.js → usage-ROIaW3wo.js} +3 -3
  51. package/dist/{web-search-provider.runtime-DCdx4DGu.js → web-search-provider.runtime-ClaNPV7N.js} +3 -3
  52. package/openclaw.plugin.json +18 -1
  53. package/package.json +8 -8
  54. package/dist/app-server-policy-bg5QPSVP.js +0 -25
@@ -1,22 +1,22 @@
1
- import { h as readCodexPluginConfig, p as isCodexRemoteExecPlacementSandbox } from "./config-CpNeo5AE.js";
2
- import { V as createCodexElicitationResponse, ct as CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE, l as releaseLeasedSharedCodexAppServerClient, ut as isJsonObject } from "./shared-client-Dw-rzii0.js";
3
- import { E as itemStatus, F as readItem, b as auditNativeToolUnfinishedStatus, h as isCodexNotificationForTurn, v as auditNativeToolName, y as auditNativeToolTerminalStatus } from "./attempt-client-cleanup-URjUlCHg.js";
4
- import { l as readCodexTurn } from "./protocol-validators-VAVyEnDp.js";
5
- import { A as isForcedPrivateQaCodexRuntime, M as isSystemAgentOnlyCodexDynamicToolAllowlist, N as normalizeCodexDynamicToolName, O as filterCodexDynamicTools, f as resolveCodexWebSearchPlan, k as filterCodexDynamicToolsForDisabledNativeSurface } from "./thread-lifecycle-DKdrnTMX.js";
1
+ import { h as readCodexPluginConfig, p as isCodexRemoteExecPlacementSandbox } from "./config-giW8X0QA.js";
2
+ import { K as createCodexElicitationResponse, gt as CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE, p as releaseLeasedSharedCodexAppServerClient, vt as isJsonObject } from "./shared-client-DKz23hHY.js";
3
+ import { E as itemStatus, F as readItem, b as auditNativeToolUnfinishedStatus, h as isCodexNotificationForTurn, v as auditNativeToolName, y as auditNativeToolTerminalStatus } from "./attempt-client-cleanup-bfsmcQ6m.js";
4
+ import { l as readCodexTurn } from "./protocol-validators-VeJxxJ0U.js";
5
+ import { $ as isForcedPrivateQaCodexRuntime, D as withDynamicToolExecutionState, E as createFailedDynamicToolResponse, F as resolveCodexWebSearchPlan, O as withDynamicToolTranscriptDetails, Q as filterCodexDynamicToolsForDisabledNativeSurface, T as resolveCodexToolAbortTerminalReason, Z as filterCodexDynamicTools, d as emitCodexNativePreToolUseFailureDiagnostic, nt as normalizeCodexDynamicToolName, tt as isSystemAgentOnlyCodexDynamicToolAllowlist } from "./thread-lifecycle-L7oEkTlq.js";
6
6
  import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-CnfoUPCE.js";
7
- import { r as formatCodexDisplayText } from "./command-formatters-D0nZ3qtd.js";
8
- import { a as resolveCodexNodeExecToolOverrides, i as resolveCodexNativeExecutionPolicy } from "./sandbox-guard-C1qEeZ5C.js";
9
- import { a as sanitizeCodexApprovalVisibleText, c as waitForPluginApprovalDecision, i as requestPluginApproval, n as codexApprovalTimeoutText, o as stripDanglingCodexApprovalTerminalSequence, r as mapExecDecisionToOutcome, s as truncateCodexApprovalDisplayText, t as approvalRequestExplicitlyUnavailable } from "./plugin-approval-roundtrip-C75Yiiwc.js";
7
+ import { r as formatCodexDisplayText } from "./command-formatters-DEsIufkt.js";
8
+ import { a as resolveCodexNodeExecToolOverrides, i as resolveCodexNativeExecutionPolicy } from "./sandbox-guard-kuWTn9C5.js";
9
+ import { a as sanitizeCodexApprovalVisibleText, c as waitForPluginApprovalDecision, i as requestPluginApproval, n as codexApprovalTimeoutText, o as stripDanglingCodexApprovalTerminalSequence, r as mapExecDecisionToOutcome, s as truncateCodexApprovalDisplayText, t as approvalRequestExplicitlyUnavailable } from "./plugin-approval-roundtrip-bUm777tR.js";
10
10
  import { asNonArrayRecord, asOptionalRecord, isRecord, normalizeOptionalString, normalizeTrimmedStringList, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
11
11
  import { validateJsonSchemaValue } from "openclaw/plugin-sdk/json-schema-runtime";
12
12
  import path, { posix } from "node:path";
13
13
  import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
14
- import { addTimerTimeoutGraceMs, asDateTimestampMs, finiteSecondsToTimerSafeMilliseconds, parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime";
14
+ import { asDateTimestampMs } from "openclaw/plugin-sdk/number-runtime";
15
15
  import { HEARTBEAT_RESPONSE_TOOL_NAME, buildAgentHookContextChannelFields, buildEmbeddedAttemptToolRunContext, consumeAdjustedParamsForToolCall, consumePreExecutionBlockedToolCall, createAgentToolResultMiddlewareRunner, createCodexAppServerToolResultExtensionRunner, embeddedAgentLog, extractMessagingToolSend, extractMessagingToolSendResult, extractToolResultMediaArtifact, filterProviderNormalizableTools, filterToolResultMediaUrls, finalizeToolTerminalPresentation, formatApprovalDisplayPath, formatToolExecutionErrorMessage, getBeforeToolCallFailureDisposition, getChannelAgentToolMeta, getPluginToolMeta, getPluginToolSideEffectOwnerKey, hasNativeHookRelayInvocation, invokeNativeHookRelay, isDeliveredMessageToolOnlySourceReplyResult, isDeliveredMessagingToolResult, isHostScopedAgentToolActive, isMessagingTool, isMessagingToolSendAction, isReplaySafeToolCall, isSubagentSessionKey, isToolResultError, isToolWrappedWithBeforeToolCallHook, normalizeAgentRuntimeTools, normalizeHeartbeatToolResponse, projectRuntimeToolInputSchema, resolveAttemptSpawnWorkspaceDir, resolveModelAuthMode, resolveNativeHookRelayDeferredToolApproval, resolveToolExecutionErrorKind, resolveToolResultFailureKind, runAgentHarnessAfterToolCallHook, sanitizeToolResult, setBeforeToolCallDiagnosticsEnabled, supportsModelTools, wrapToolWithBeforeToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
16
16
  import { createHash, randomUUID } from "node:crypto";
17
17
  import { resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
18
- import { killProcessTree } from "openclaw/plugin-sdk/process-runtime";
19
18
  import { formatErrorMessage as formatErrorMessage$1, redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
19
+ import { killProcessTree } from "openclaw/plugin-sdk/process-runtime";
20
20
  import { SsrFBlockedError, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
21
21
  import { spawn } from "node:child_process";
22
22
  import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
@@ -29,790 +29,11 @@ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
29
29
  import { redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
30
30
  import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
31
31
  import { runWithCronCreatorAuthorityCapabilityResolver } from "openclaw/plugin-sdk/codex-mcp-projection";
32
- import { emitTrustedDiagnosticEvent, hasPendingInternalDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
32
+ import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
33
33
  import { buildRemoteCommand, isToolAllowed, sanitizeEnvVars } from "openclaw/plugin-sdk/sandbox";
34
34
  import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
35
- import { registerRetainedNativeHookRelayForBundledRuntime } from "openclaw/plugin-sdk/native-hook-relay-runtime";
36
35
  import { getMediaDir } from "openclaw/plugin-sdk/media-runtime";
37
36
  import { normalizeOpenAIToolSchemas } from "openclaw/plugin-sdk/provider-tools";
38
- //#region extensions/codex/src/app-server/dynamic-tool-response-state.ts
39
- /** Retains the host-owned app preview without adding it to Codex's response payload. */
40
- function withDynamicToolTranscriptDetails(response, details) {
41
- if (details === void 0) return response;
42
- Object.defineProperty(response, "transcriptDetails", {
43
- configurable: true,
44
- enumerable: false,
45
- value: details
46
- });
47
- return response;
48
- }
49
- function withDynamicToolTerminalResolution(response, terminalResolution) {
50
- if (terminalResolution) {
51
- Object.defineProperties(response, {
52
- terminalResolution: {
53
- configurable: true,
54
- enumerable: false,
55
- value: terminalResolution
56
- },
57
- executionStarted: {
58
- configurable: true,
59
- enumerable: false,
60
- value: terminalResolution.executionStarted
61
- },
62
- ...terminalResolution.executedArguments ? { executedArguments: {
63
- configurable: true,
64
- enumerable: false,
65
- value: terminalResolution.executedArguments
66
- } } : {}
67
- });
68
- withDynamicToolSideEffectEvidence(response, terminalResolution.sideEffectEvidence);
69
- }
70
- return response;
71
- }
72
- function withDynamicToolExecutionState(response, state) {
73
- Object.defineProperties(response, {
74
- executedArguments: {
75
- configurable: true,
76
- enumerable: false,
77
- value: state.executedArguments
78
- },
79
- executionStarted: {
80
- configurable: true,
81
- enumerable: false,
82
- value: state.executionStarted
83
- }
84
- });
85
- return withDynamicToolSideEffectEvidence(response, state.sideEffectEvidence === true);
86
- }
87
- function withDynamicToolSideEffectEvidence(response, sideEffectEvidence) {
88
- if (!sideEffectEvidence) {
89
- delete response.sideEffectEvidence;
90
- return response;
91
- }
92
- Object.defineProperty(response, "sideEffectEvidence", {
93
- configurable: true,
94
- enumerable: false,
95
- value: true
96
- });
97
- return response;
98
- }
99
- function createFailedDynamicToolResponse(message, options) {
100
- const response = {
101
- contentItems: [{
102
- type: "inputText",
103
- text: message
104
- }],
105
- success: false
106
- };
107
- Object.defineProperties(response, {
108
- diagnosticTerminalReason: {
109
- configurable: true,
110
- enumerable: false,
111
- value: options?.terminalReason ?? "failed"
112
- },
113
- diagnosticTerminalType: {
114
- configurable: true,
115
- enumerable: false,
116
- value: "error"
117
- }
118
- });
119
- if (options?.executionStarted !== void 0) Object.defineProperty(response, "executionStarted", {
120
- configurable: true,
121
- enumerable: false,
122
- value: options.executionStarted
123
- });
124
- if (options?.executedArguments !== void 0) Object.defineProperty(response, "executedArguments", {
125
- configurable: true,
126
- enumerable: false,
127
- value: options.executedArguments
128
- });
129
- return withDynamicToolSideEffectEvidence(response, options?.sideEffectEvidence === true);
130
- }
131
- //#endregion
132
- //#region extensions/codex/src/app-server/tool-abort-terminal-reason.ts
133
- /** Leaf helper shared by native and dynamic tool diagnostics. */
134
- const CODEX_TIMEOUT_ABORT_REASONS = /* @__PURE__ */ new Set([
135
- "codex_startup_timeout",
136
- "turn_completion_idle_timeout",
137
- "turn_progress_idle_timeout",
138
- "turn_terminal_idle_timeout"
139
- ]);
140
- /** Preserves timeout provenance when an enclosing run aborts an active tool. */
141
- function resolveCodexToolAbortTerminalReason(signal) {
142
- try {
143
- const reason = signal.reason;
144
- if (typeof reason === "string") {
145
- if (CODEX_TIMEOUT_ABORT_REASONS.has(reason)) return "timed_out";
146
- return reason === "client_closed" ? "failed" : "cancelled";
147
- }
148
- if (reason && typeof reason === "object") {
149
- const record = reason;
150
- if (record.name === "TimeoutError" || record.reason === "timeout") return "timed_out";
151
- }
152
- } catch {
153
- return "cancelled";
154
- }
155
- return "cancelled";
156
- }
157
- //#endregion
158
- //#region extensions/codex/src/app-server/dynamic-tool-execution.ts
159
- /**
160
- * Timeout, terminal-release, and diagnostic helpers for Codex dynamic tool
161
- * calls.
162
- */
163
- /** Default timeout for Codex dynamic tool calls. */
164
- const CODEX_DYNAMIC_TOOL_TIMEOUT_MS = 9e4;
165
- /** Hard cap for per-call Codex dynamic tool timeout overrides. */
166
- const CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS = 6e5;
167
- const CODEX_DYNAMIC_TOOL_TIMEOUT_SECONDS_GRACE_MS = 3e4;
168
- const CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12e4;
169
- const CODEX_DYNAMIC_COMPUTER_GATEWAY_TIMEOUT_MS = 3e4;
170
- const CODEX_DYNAMIC_COMPUTER_COMPLETION_GRACE_MS = 3e4;
171
- /** Timeout for image-understanding style dynamic tool calls. */
172
- const CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS = 6e4;
173
- /** Timeout for message-delivery dynamic tool calls. */
174
- const CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS = 6e5;
175
- /** Outer default for collector waits: full swarm budget plus completion grace. */
176
- const CODEX_DYNAMIC_AGENTS_WAIT_TOOL_TIMEOUT_MS = 63e4;
177
- const LOG_FIELD_MAX_LENGTH = 160;
178
- function normalizeLogField(value) {
179
- if (typeof value !== "string") return;
180
- const normalized = value.replaceAll(String.fromCharCode(27), " ").replaceAll("\r", " ").replaceAll("\n", " ").replaceAll(" ", " ").trim();
181
- if (!normalized) return;
182
- return normalized.length > LOG_FIELD_MAX_LENGTH ? `${truncateUtf16Safe(normalized, LOG_FIELD_MAX_LENGTH - 3)}...` : normalized;
183
- }
184
- function readNumericTimeoutMs(value) {
185
- if (typeof value === "number" && Number.isFinite(value)) return Math.max(0, Math.floor(value));
186
- if (typeof value === "string") {
187
- const parsed = parseStrictNonNegativeInteger(value);
188
- if (parsed !== void 0) return Math.max(0, Math.floor(parsed));
189
- }
190
- }
191
- function formatDynamicToolTimeoutDetails(params) {
192
- const tool = normalizeLogField(params.call.tool) ?? "unknown";
193
- const baseMeta = {
194
- tool: params.call.tool,
195
- toolCallId: params.call.callId,
196
- threadId: params.call.threadId,
197
- turnId: params.call.turnId,
198
- timeoutMs: params.timeoutMs,
199
- timeoutKind: "codex_dynamic_tool_rpc"
200
- };
201
- if (tool !== "process" || !isJsonObject(params.call.arguments)) return {
202
- responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms while running tool ${tool}.`,
203
- consoleMessage: `codex dynamic tool timeout: tool=${tool} toolTimeoutMs=${params.timeoutMs}; per-tool-call watchdog, not session idle`,
204
- meta: baseMeta
205
- };
206
- const action = normalizeLogField(params.call.arguments.action);
207
- const sessionId = normalizeLogField(params.call.arguments.sessionId);
208
- const requestedTimeoutMs = readNumericTimeoutMs(params.call.arguments.timeout);
209
- const actionPart = action ? ` action=${action}` : "";
210
- const sessionPart = sessionId ? ` sessionId=${sessionId}` : "";
211
- const requestedPart = requestedTimeoutMs === void 0 ? "" : ` requestedWaitMs=${requestedTimeoutMs}`;
212
- const retryHint = action === "poll" ? "; repeated lines usually mean process-poll retry churn, not model progress" : "";
213
- const responseTarget = action || sessionId ? ` while waiting for process${actionPart}${sessionPart}` : " while waiting for the process tool";
214
- return {
215
- responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms${responseTarget}. This is a tool RPC timeout, not a session idle timeout.`,
216
- consoleMessage: `codex process tool timeout:${actionPart}${sessionPart} toolTimeoutMs=${params.timeoutMs}${requestedPart}; per-tool-call watchdog, not session idle${retryHint}`,
217
- meta: {
218
- ...baseMeta,
219
- processAction: action,
220
- processSessionId: sessionId,
221
- processRequestedTimeoutMs: requestedTimeoutMs
222
- }
223
- };
224
- }
225
- /**
226
- * Runs a dynamic tool call with run-abort and per-call timeout handling,
227
- * returning a Codex protocol response instead of throwing.
228
- */
229
- async function handleDynamicToolCallWithTimeout(params) {
230
- let didNotifyAgentToolResult = false;
231
- const conservativeRaceResponses = /* @__PURE__ */ new WeakSet();
232
- const finalizeTerminal = (response) => {
233
- const executionSnapshot = params.toolBridge.consumeToolExecutionSnapshot?.(params.call.callId);
234
- const ownerKey = params.toolBridge.sideEffectOwnerKeyForTool?.(params.call.tool);
235
- const observedExecutionStarted = executionSnapshot?.executionStarted ?? (conservativeRaceResponses.has(response) ? void 0 : response.executionStarted);
236
- const terminalResolution = params.observeToolTerminal?.({
237
- toolCallId: params.call.callId,
238
- toolName: params.call.tool,
239
- arguments: response.executedArguments ?? executionSnapshot?.executedArguments ?? params.call.arguments,
240
- ...params.toolMeta ? { meta: params.toolMeta } : {},
241
- ...ownerKey ? { ownerMutation: { ownerKey } } : {},
242
- ...observedExecutionStarted !== void 0 ? { executionStarted: observedExecutionStarted } : {},
243
- outcome: response.success ? "success" : "failure",
244
- ...!response.success ? { failure: { error: readDynamicToolResponseText(response) } } : {}
245
- });
246
- return withDynamicToolTerminalResolution(response, terminalResolution);
247
- };
248
- const createFailedAfterPossibleDispatch = (message, terminalReason) => {
249
- const response = createFailedDynamicToolResponse(message, {
250
- executionStarted: true,
251
- sideEffectEvidence: true,
252
- terminalReason
253
- });
254
- conservativeRaceResponses.add(response);
255
- return response;
256
- };
257
- const notifyAgentToolResult = (event) => {
258
- if (didNotifyAgentToolResult) return;
259
- didNotifyAgentToolResult = true;
260
- try {
261
- params.onAgentToolResult?.(event);
262
- } catch (error) {
263
- embeddedAgentLog.warn(`onAgentToolResult handler failed: tool=${params.call.tool} error=${String(error)}`);
264
- }
265
- };
266
- const notifyFailedToolResult = (message, terminalReason = "failed") => {
267
- notifyAgentToolResult({
268
- toolName: params.call.tool,
269
- result: {
270
- content: [{
271
- type: "text",
272
- text: message
273
- }],
274
- details: {
275
- status: terminalReason,
276
- error: message
277
- }
278
- },
279
- isError: true
280
- });
281
- };
282
- if (params.signal.aborted) {
283
- const message = "OpenClaw dynamic tool call aborted before execution.";
284
- const terminalReason = resolveCodexToolAbortTerminalReason(params.signal);
285
- params.onFallbackSelected?.();
286
- notifyFailedToolResult(message, terminalReason);
287
- return finalizeTerminal(createFailedDynamicToolResponse(message, {
288
- executionStarted: false,
289
- terminalReason
290
- }));
291
- }
292
- const controller = new AbortController();
293
- let timeout;
294
- let timedOut = false;
295
- let resolveAbort;
296
- const abortFromRun = () => {
297
- const message = "OpenClaw dynamic tool call aborted.";
298
- const terminalReason = resolveCodexToolAbortTerminalReason(params.signal);
299
- params.onFallbackSelected?.();
300
- controller.abort(params.signal.reason ?? /* @__PURE__ */ new Error(message));
301
- notifyFailedToolResult(message, terminalReason);
302
- resolveAbort?.(createFailedAfterPossibleDispatch(message, terminalReason));
303
- };
304
- const abortPromise = new Promise((resolve) => {
305
- resolveAbort = resolve;
306
- });
307
- const timeoutPromise = new Promise((resolve) => {
308
- const timeoutMs = clampDynamicToolTimeoutMs(params.timeoutMs);
309
- timeout = setTimeout(() => {
310
- timedOut = true;
311
- const timeoutDetails = formatDynamicToolTimeoutDetails({
312
- call: params.call,
313
- timeoutMs
314
- });
315
- params.onFallbackSelected?.();
316
- controller.abort(new Error(timeoutDetails.responseMessage));
317
- params.onTimeout?.();
318
- embeddedAgentLog.warn("codex dynamic tool call timed out", {
319
- ...timeoutDetails.meta,
320
- consoleMessage: timeoutDetails.consoleMessage
321
- });
322
- notifyFailedToolResult(timeoutDetails.responseMessage, "timed_out");
323
- resolve(createFailedAfterPossibleDispatch(timeoutDetails.responseMessage, "timed_out"));
324
- }, timeoutMs);
325
- timeout.unref?.();
326
- });
327
- try {
328
- params.signal.addEventListener("abort", abortFromRun, { once: true });
329
- if (params.signal.aborted) abortFromRun();
330
- const response = await Promise.race([
331
- params.toolBridge.handleToolCall(params.call, {
332
- signal: controller.signal,
333
- onAgentToolResult: notifyAgentToolResult,
334
- toolCallOrdinal: params.toolCallOrdinal,
335
- retainExecutionSnapshot: true
336
- }),
337
- abortPromise,
338
- timeoutPromise
339
- ]);
340
- if (!response.success && !didNotifyAgentToolResult) notifyFailedToolResult(readDynamicToolResponseText(response), response.diagnosticTerminalReason ?? "failed");
341
- return finalizeTerminal(response);
342
- } catch (error) {
343
- const terminalReason = params.signal.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : resolveToolExecutionErrorKind(error);
344
- const message = formatToolExecutionErrorMessage(error, "OpenClaw dynamic tool call failed.");
345
- notifyFailedToolResult(message, terminalReason);
346
- return finalizeTerminal(createFailedAfterPossibleDispatch(message, terminalReason));
347
- } finally {
348
- if (timeout) clearTimeout(timeout);
349
- params.signal.removeEventListener("abort", abortFromRun);
350
- resolveAbort = void 0;
351
- if (!timedOut && !controller.signal.aborted) controller.abort(/* @__PURE__ */ new Error("OpenClaw dynamic tool call finished."));
352
- }
353
- }
354
- function readDynamicToolResponseText(response) {
355
- return response.contentItems.flatMap((item) => item.type === "inputText" && typeof item.text === "string" ? [item.text] : []).join("\n").trim() || "OpenClaw dynamic tool call failed.";
356
- }
357
- /** Strips OpenClaw-only metadata before sending a dynamic tool response to Codex. */
358
- function toCodexDynamicToolProtocolResponse(response) {
359
- return {
360
- contentItems: response.contentItems,
361
- success: response.success
362
- };
363
- }
364
- /** Adds async-started progress details when a tool result continues out of band. */
365
- function toCodexDynamicToolProgressResponse(response, protocolResponse) {
366
- const transcriptDetails = isJsonObject(response.transcriptDetails) ? response.transcriptDetails : void 0;
367
- const mcpAppPreview = isJsonObject(transcriptDetails?.mcpAppPreview) ? transcriptDetails.mcpAppPreview : void 0;
368
- const progressDetails = mcpAppPreview ? { mcpAppPreview } : void 0;
369
- if (response.asyncStarted !== true && progressDetails === void 0) return protocolResponse;
370
- return {
371
- ...protocolResponse,
372
- ...progressDetails ? { details: progressDetails } : {},
373
- ...response.asyncStarted === true ? { details: {
374
- ...progressDetails,
375
- async: true,
376
- status: "started"
377
- } } : {}
378
- };
379
- }
380
- /** Decides whether a terminal dynamic tool response can release the Codex turn. */
381
- function shouldReleaseTurnAfterTerminalDynamicTool(state) {
382
- return !state.completed && !state.aborted && state.responseSuccess && !state.currentTurnHadNonTerminalDynamicToolResult && state.activeAppServerTurnRequests === 0 && state.activeTurnItemIdsCount === 0 && state.pendingOpenClawDynamicToolCompletionIdsCount === 0;
383
- }
384
- /** Returns true when a non-async result should block terminal-release shortcuts. */
385
- function shouldBlockTerminalReleaseForNonTerminalDynamicToolResult(response) {
386
- return response.asyncStarted !== true;
387
- }
388
- /** Resolves whether terminal diagnostic state should release, wait, or stay idle. */
389
- function resolveTerminalDynamicToolBatchAction(state) {
390
- if (state.activeAppServerTurnRequests > 0 || state.activeTurnItemIdsCount > 0 || state.pendingOpenClawDynamicToolCompletionIdsCount > 0) return "wait";
391
- if (state.currentTurnHadNonTerminalDynamicToolResult) return "clear-nonterminal-batch";
392
- if (state.hasPendingTerminalDynamicToolRelease) return "release-pending-terminal";
393
- return "idle";
394
- }
395
- /** Returns true for diagnostic events that terminate a dynamic tool call. */
396
- function isDynamicToolTerminalDiagnosticEvent(event) {
397
- return event.type === "tool.execution.completed" || event.type === "tool.execution.error" || event.type === "tool.execution.blocked";
398
- }
399
- /** Matches terminal diagnostics to a specific dynamic tool call id/name. */
400
- function isMatchingDynamicToolTerminalDiagnostic(params) {
401
- if (params.event.toolCallId !== params.call.callId || params.event.toolName !== params.call.tool) return false;
402
- if (params.runId !== void 0) return params.event.runId === params.runId;
403
- if (params.sessionId !== void 0) return params.event.sessionId === params.sessionId;
404
- if (params.sessionKey !== void 0) return params.event.sessionKey === params.sessionKey;
405
- return params.event.runId === void 0 && params.event.sessionId === void 0 && params.event.sessionKey === void 0;
406
- }
407
- /** Checks pending diagnostics for a terminal event matching a tool call. */
408
- function hasPendingDynamicToolTerminalDiagnostic(params) {
409
- return hasPendingInternalDiagnosticEvent((event) => {
410
- if (!isDynamicToolTerminalDiagnosticEvent(event)) return false;
411
- return isMatchingDynamicToolTerminalDiagnostic({
412
- event,
413
- call: params.call,
414
- runId: params.runId,
415
- sessionId: params.sessionId,
416
- sessionKey: params.sessionKey
417
- });
418
- });
419
- }
420
- /** Resolves per-tool timeout, applying media/message defaults and hard caps. */
421
- function resolveDynamicToolCallTimeoutMs(params) {
422
- if (params.call.tool === "computer") return clampDynamicToolTimeoutMs(readComputerToolTimeoutMs(params.call.arguments));
423
- if (params.call.tool === "message") return CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS;
424
- if (params.call.tool === "agents_wait") {
425
- const requestedMs = readDynamicToolCallTimeoutMs(params.call.arguments) ?? readConfiguredDynamicToolTimeoutMs(params.call.tool, params.config) ?? CODEX_DYNAMIC_AGENTS_WAIT_TOOL_TIMEOUT_MS;
426
- return Math.max(1, Math.min(63e4, Math.floor(requestedMs)));
427
- }
428
- return clampDynamicToolTimeoutMs(readDynamicToolCallTimeoutMs(params.call.arguments) ?? readConfiguredDynamicToolTimeoutMs(params.call.tool, params.config) ?? CODEX_DYNAMIC_TOOL_TIMEOUT_MS);
429
- }
430
- function readComputerToolTimeoutMs(value) {
431
- const args = isJsonObject(value) ? value : void 0;
432
- const action = typeof args?.action === "string" ? args.action : void 0;
433
- const gatewayTimeoutMs = readPositiveFiniteTimeoutMs(args?.timeoutMs) ?? CODEX_DYNAMIC_COMPUTER_GATEWAY_TIMEOUT_MS;
434
- const gatewayCallCount = action === "screenshot" || action === "wait" ? 3 : 4;
435
- return (action === "wait" || action === "hold_key" ? Math.max(0, Number(args?.duration) || 0) * 1e3 : 0) + gatewayCallCount * gatewayTimeoutMs + CODEX_DYNAMIC_COMPUTER_COMPLETION_GRACE_MS;
436
- }
437
- function readDynamicToolCallTimeoutMs(value) {
438
- if (!isJsonObject(value)) return;
439
- const timeoutMs = readPositiveFiniteTimeoutMs(value.timeoutMs);
440
- if (timeoutMs !== void 0) return timeoutMs;
441
- const timeoutSecondsMs = readDynamicToolTimeoutSecondsAsMs(value.timeoutSeconds);
442
- return timeoutSecondsMs === void 0 ? void 0 : addTimerTimeoutGraceMs(timeoutSecondsMs, CODEX_DYNAMIC_TOOL_TIMEOUT_SECONDS_GRACE_MS);
443
- }
444
- function readConfiguredDynamicToolTimeoutMs(toolName, config) {
445
- if (toolName === "image_generate") {
446
- const imageModel = config?.agents?.defaults?.mediaModels?.image;
447
- if (!imageModel || typeof imageModel !== "object") return CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
448
- return readPositiveFiniteTimeoutMs(imageModel.timeoutMs) ?? CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
449
- }
450
- if (toolName === "view_image") {
451
- const candidates = (config?.tools?.media?.models ?? []).filter((entry) => !entry.capabilities || entry.capabilities.includes("image"));
452
- const capabilityTimeoutMs = readTimeoutSecondsAsMs(config?.tools?.media?.image?.timeoutSeconds);
453
- return Math.max(capabilityTimeoutMs ?? CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS, ...candidates.map((entry) => readTimeoutSecondsAsMs(entry.timeoutSeconds) ?? capabilityTimeoutMs ?? CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS));
454
- }
455
- if (toolName === "message") return CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS;
456
- }
457
- function readTimeoutSecondsAsMs(value) {
458
- const seconds = readPositiveFiniteTimeoutMs(value);
459
- return seconds === void 0 ? void 0 : seconds * 1e3;
460
- }
461
- function readDynamicToolTimeoutSecondsAsMs(value) {
462
- if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) return;
463
- return value * 1e3;
464
- }
465
- function readPositiveFiniteTimeoutMs(value) {
466
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
467
- }
468
- function clampDynamicToolTimeoutMs(timeoutMs) {
469
- return Math.max(1, Math.min(CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS, Math.floor(timeoutMs)));
470
- }
471
- //#endregion
472
- //#region extensions/codex/src/app-server/native-hook-relay-state.ts
473
- const pending = /* @__PURE__ */ new Set();
474
- /** Owns delayed hook-relay cleanup across runtime scheduling and test teardown. */
475
- const nativeHookRelayUnregisterQueue = {
476
- add(entry) {
477
- pending.add(entry);
478
- },
479
- delete(entry) {
480
- return pending.delete(entry);
481
- },
482
- flush() {
483
- while (pending.size > 0) {
484
- const entry = pending.values().next().value;
485
- if (!entry) return;
486
- clearTimeout(entry.timeout);
487
- entry.unregister();
488
- }
489
- },
490
- clear() {
491
- for (const entry of pending) clearTimeout(entry.timeout);
492
- pending.clear();
493
- }
494
- };
495
- //#endregion
496
- //#region extensions/codex/src/app-server/native-hook-relay.ts
497
- /**
498
- * Bridges Codex native hook callbacks into OpenClaw's native hook relay so
499
- * app-server tool events can still run OpenClaw policy and diagnostics.
500
- */
501
- /** Codex hook events that can be registered through OpenClaw's native relay. */
502
- const CODEX_NATIVE_HOOK_RELAY_EVENTS = [
503
- "pre_tool_use",
504
- "post_tool_use",
505
- "permission_request",
506
- "before_agent_finalize"
507
- ];
508
- const CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS = CODEX_NATIVE_HOOK_RELAY_EVENTS.filter((event) => event !== "permission_request");
509
- const CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS = 30 * 6e4;
510
- /** Extra relay lifetime after the expected turn budget, preventing late hook drops. */
511
- const CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS = 5 * 6e4;
512
- const CODEX_NATIVE_HOOK_RELAY_COMMAND_MIN_PARENT_MARGIN_MS = 250;
513
- const CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS = 1e3;
514
- const CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC = 10;
515
- const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS = 1e4;
516
- const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS = 5e3;
517
- const MAX_PENDING_DIRECT_CHILD_ADMISSIONS = 32;
518
- const CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID = {
519
- exec: [
520
- "Bash",
521
- "exec",
522
- "exec_command"
523
- ],
524
- apply_patch: [
525
- "apply_patch",
526
- "Write",
527
- "Edit"
528
- ],
529
- spawn_agent: ["spawn_agent", "Agent"]
530
- };
531
- /** Defers relay unregister so late native hook subprocesses can still resolve. */
532
- function scheduleCodexNativeHookRelayUnregister(params) {
533
- let pending;
534
- const unregister = () => {
535
- if (!pending) return;
536
- const current = pending;
537
- pending = void 0;
538
- if (!nativeHookRelayUnregisterQueue.delete(current)) return;
539
- params.relay.unregister();
540
- };
541
- const timeout = setTimeout(unregister, resolveCodexNativeHookRelayUnregisterGraceMs(params.hookTimeoutSec));
542
- pending = {
543
- timeout,
544
- unregister
545
- };
546
- nativeHookRelayUnregisterQueue.add(pending);
547
- timeout.unref();
548
- }
549
- /** Computes the delayed unregister window from Codex's hook timeout. */
550
- function resolveCodexNativeHookRelayUnregisterGraceMs(hookTimeoutSec) {
551
- const hookTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 0;
552
- return Math.max(CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS, addTimerTimeoutGraceMs(hookTimeoutMs, CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS) ?? 0);
553
- }
554
- /** Records a native pre-tool failure that Codex does not project as a tool item. */
555
- function emitCodexNativePreToolUseFailureDiagnostic(params) {
556
- emitTrustedDiagnosticEvent({
557
- type: "tool.execution.error",
558
- ...params.agentId ? { agentId: params.agentId } : {},
559
- sessionId: params.sessionId,
560
- ...params.sessionKey ? { sessionKey: params.sessionKey } : {},
561
- runId: params.runId,
562
- toolName: params.failure.toolName,
563
- toolCallId: params.failure.toolCallId,
564
- durationMs: params.failure.durationMs,
565
- errorCategory: "before_tool_call",
566
- terminalReason: params.terminalReason ?? (params.signal?.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : params.failure.disposition),
567
- ...params.sourceTimestampMs !== void 0 ? { sourceTimestampMs: params.sourceTimestampMs } : {}
568
- });
569
- }
570
- /** Registers an OpenClaw native hook relay for a Codex app-server turn. */
571
- function createCodexNativeHookRelay(params) {
572
- if (params.options?.enabled === false) return;
573
- const directChildClaims = /* @__PURE__ */ new Map();
574
- const pendingDirectChildAdmissions = /* @__PURE__ */ new Map();
575
- let foregroundClosed = false;
576
- let successfulYieldRetentionAuthorized = false;
577
- const assertClaim = (threadId, claim) => () => directChildClaims.get(threadId) === claim;
578
- const rejectPendingAdmissions = (reason) => {
579
- for (const pending of pendingDirectChildAdmissions.values()) pending.reject(new Error(reason));
580
- pendingDirectChildAdmissions.clear();
581
- };
582
- const relay = registerRetainedNativeHookRelayForBundledRuntime({
583
- provider: "codex",
584
- relayId: buildCodexNativeHookRelayId({
585
- agentId: params.agentId,
586
- sessionId: params.sessionId,
587
- sessionKey: params.sessionKey
588
- }),
589
- ...params.generation ? { generation: params.generation } : {},
590
- ...params.generationMismatchGraceMs ? { generationMismatchGraceMs: params.generationMismatchGraceMs } : {},
591
- ...params.agentId ? { agentId: params.agentId } : {},
592
- sessionId: params.sessionId,
593
- ...params.sessionKey ? { sessionKey: params.sessionKey } : {},
594
- ...params.config ? { config: params.config } : {},
595
- runId: params.runId,
596
- ...params.channelId ? { channelId: params.channelId } : {},
597
- ...params.requester ? { requester: params.requester } : {},
598
- ...params.approvalContext ? { approvalContext: params.approvalContext } : {},
599
- allowedEvents: params.events,
600
- preToolUseLoopDetection: params.loopDetectionPreToolUseRelay,
601
- ttlMs: resolveCodexNativeHookRelayTtlMs({
602
- explicitTtlMs: params.options?.ttlMs,
603
- attemptTimeoutMs: params.attemptTimeoutMs,
604
- startupTimeoutMs: params.startupTimeoutMs,
605
- turnStartTimeoutMs: params.turnStartTimeoutMs
606
- }),
607
- signal: params.signal,
608
- runBeforeToolCall: params.hostCapabilities.runBeforeToolCall,
609
- assertActive: params.hostCapabilities.assertActive,
610
- retention: {
611
- readClaim: readCodexNativeChildThreadId,
612
- shouldRetainAfterForegroundClose: () => successfulYieldRetentionAuthorized && directChildClaims.size > 0,
613
- allowPreToolUse: (childThreadId) => directChildClaims.has(childThreadId),
614
- awaitForegroundAdmission: (childThreadId) => {
615
- if (foregroundClosed) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission unavailable"));
616
- const existingClaim = directChildClaims.get(childThreadId);
617
- if (existingClaim) return Promise.resolve(assertClaim(childThreadId, existingClaim));
618
- const existingPending = pendingDirectChildAdmissions.get(childThreadId);
619
- if (existingPending) return existingPending.promise.then((claim) => assertClaim(childThreadId, claim));
620
- if (pendingDirectChildAdmissions.size >= MAX_PENDING_DIRECT_CHILD_ADMISSIONS) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission capacity reached"));
621
- const { promise, resolve, reject } = createDeferred();
622
- pendingDirectChildAdmissions.set(childThreadId, {
623
- promise,
624
- resolve,
625
- reject
626
- });
627
- return promise.then((claim) => assertClaim(childThreadId, claim));
628
- },
629
- onDispose: () => {
630
- foregroundClosed = true;
631
- rejectPendingAdmissions("native hook relay registration closed");
632
- }
633
- },
634
- onPreToolUseFailure: params.onPreToolUseFailure,
635
- command: {
636
- nice: 10,
637
- timeoutMs: params.options?.gatewayTimeoutMs
638
- }
639
- });
640
- const unregister = () => {
641
- foregroundClosed = true;
642
- rejectPendingAdmissions("native hook relay foreground closed");
643
- relay.unregister();
644
- };
645
- return {
646
- ...relay,
647
- unregister,
648
- authorizeRetentionAfterSuccessfulYield: () => {
649
- successfulYieldRetentionAuthorized = true;
650
- },
651
- hasClaimedDirectChild: () => directChildClaims.size > 0,
652
- rejectPendingDirectChild: (threadIdInput, reason) => {
653
- const threadId = threadIdInput.trim();
654
- const pending = threadId ? pendingDirectChildAdmissions.get(threadId) : void 0;
655
- if (!pending) return;
656
- pendingDirectChildAdmissions.delete(threadId);
657
- pending.reject(new Error(reason));
658
- },
659
- claimDirectChild: (threadIdInput) => {
660
- const threadId = threadIdInput.trim();
661
- if (!threadId) return () => void 0;
662
- if (directChildClaims.get(threadId)) return () => void 0;
663
- const claim = Symbol(threadId);
664
- directChildClaims.set(threadId, claim);
665
- const pending = pendingDirectChildAdmissions.get(threadId);
666
- pendingDirectChildAdmissions.delete(threadId);
667
- pending?.resolve(claim);
668
- let released = false;
669
- return () => {
670
- if (released) return;
671
- released = true;
672
- if (directChildClaims.get(threadId) !== claim) return;
673
- directChildClaims.delete(threadId);
674
- if (foregroundClosed && directChildClaims.size === 0) relay.unregister();
675
- };
676
- }
677
- };
678
- }
679
- function readCodexNativeChildThreadId(rawPayload) {
680
- if (!isJsonObject(rawPayload) || typeof rawPayload.agent_id !== "string") return;
681
- return rawPayload.agent_id.trim() || void 0;
682
- }
683
- /** Selects the native hook events Codex should install for the current approval mode. */
684
- function resolveCodexNativeHookRelayEvents(params) {
685
- if (params.configuredEvents?.length) return params.configuredEvents;
686
- return params.appServer.approvalPolicy === "never" ? CODEX_NATIVE_HOOK_RELAY_EVENTS : CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS;
687
- }
688
- /** Derives the native hook relay TTL from the turn budget unless explicitly configured. */
689
- function resolveCodexNativeHookRelayTtlMs(params) {
690
- if (params.explicitTtlMs !== void 0) return params.explicitTtlMs;
691
- const relayBudgetMs = params.attemptTimeoutMs + params.startupTimeoutMs + params.turnStartTimeoutMs + CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS;
692
- return Math.max(CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS, Math.floor(relayBudgetMs));
693
- }
694
- /** Builds a stable relay id scoped to the agent and session identity. */
695
- function buildCodexNativeHookRelayId(params) {
696
- const hash = createHash("sha256");
697
- hash.update("openclaw:codex:native-hook-relay:v1");
698
- hash.update("\0");
699
- hash.update(params.agentId?.trim() || "");
700
- hash.update("\0");
701
- hash.update(params.sessionKey?.trim() || params.sessionId);
702
- return `codex-${hash.digest("hex").slice(0, 40)}`;
703
- }
704
- const CODEX_HOOK_EVENT_BY_NATIVE_EVENT = {
705
- pre_tool_use: "PreToolUse",
706
- post_tool_use: "PostToolUse",
707
- permission_request: "PermissionRequest",
708
- before_agent_finalize: "Stop"
709
- };
710
- const CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT = {
711
- pre_tool_use: "pre_tool_use",
712
- post_tool_use: "post_tool_use",
713
- permission_request: "permission_request",
714
- before_agent_finalize: "stop"
715
- };
716
- const CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS = ["/<session-flags>/config.toml", "<session-flags>/config.toml"];
717
- /** Builds the Codex config overlay that installs trusted command hooks for relay events. */
718
- function buildCodexNativeHookRelayConfig(params) {
719
- const events = params.events?.length ? params.events : CODEX_NATIVE_HOOK_RELAY_EVENTS;
720
- const selectedEvents = new Set(events);
721
- const config = { "features.hooks": true };
722
- const hookState = {};
723
- for (const event of CODEX_NATIVE_HOOK_RELAY_EVENTS) {
724
- const codexEvent = CODEX_HOOK_EVENT_BY_NATIVE_EVENT[event];
725
- const selected = selectedEvents.has(event);
726
- const shouldRelay = params.relay.shouldRelayEvent(event);
727
- if (!selected || !shouldRelay) {
728
- if (selected || params.clearOmittedEvents) config[`hooks.${codexEvent}`] = [];
729
- if (params.clearOmittedEvents) for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = { enabled: false };
730
- continue;
731
- }
732
- const timeout = normalizeHookTimeoutSec(params.hookTimeoutSec);
733
- const command = params.relay.commandForEvent(event, { timeoutMs: resolveCodexNativeHookRelayCommandTimeoutMs(timeout) });
734
- const matcher = buildCodexNativeToolMatcher(params.relay.toolMatcherForEvent(event));
735
- config[`hooks.${codexEvent}`] = [{
736
- ...matcher ? { matcher } : {},
737
- hooks: [{
738
- type: "command",
739
- command,
740
- timeout,
741
- async: false,
742
- statusMessage: "OpenClaw native hook relay"
743
- }]
744
- }];
745
- const state = {
746
- enabled: true,
747
- trusted_hash: codexCommandHookTrustedHash({
748
- event,
749
- command,
750
- matcher,
751
- timeout,
752
- statusMessage: "OpenClaw native hook relay"
753
- })
754
- };
755
- for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = state;
756
- }
757
- config["hooks.state"] = hookState;
758
- return config;
759
- }
760
- /** Builds a Codex config overlay that disables native hooks and clears hook arrays. */
761
- function buildCodexNativeHookRelayDisabledConfig() {
762
- return {
763
- "features.hooks": false,
764
- "hooks.PreToolUse": [],
765
- "hooks.PostToolUse": [],
766
- "hooks.PermissionRequest": [],
767
- "hooks.Stop": []
768
- };
769
- }
770
- function normalizeHookTimeoutSec(value) {
771
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.ceil(value) : CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC;
772
- }
773
- function resolveCodexNativeHookRelayCommandTimeoutMs(hookTimeoutSec) {
774
- const parentTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 5e3;
775
- 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)));
776
- return Math.max(1, parentTimeoutMs - parentMarginMs);
777
- }
778
- function buildCodexNativeToolMatcher(toolNames) {
779
- if (toolNames === void 0) return;
780
- if (toolNames.length === 0) throw new TypeError("Codex native hook matcher requires at least one tool name");
781
- const nativeNames = /* @__PURE__ */ new Set();
782
- let hasCustomToolName = false;
783
- for (const toolName of toolNames) {
784
- const canonicalToolName = toolName.trim();
785
- if (!canonicalToolName || canonicalToolName === "*") throw new TypeError("Codex native hook matcher requires canonical OpenClaw tool ids");
786
- const nativeAliases = CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID[canonicalToolName];
787
- if (!nativeAliases) hasCustomToolName = true;
788
- for (const nativeName of nativeAliases ?? [canonicalToolName]) nativeNames.add(nativeName);
789
- }
790
- const sortedNames = Array.from(nativeNames).toSorted();
791
- if (!hasCustomToolName && sortedNames.every((toolName) => /^[A-Za-z0-9_]+$/.test(toolName))) return sortedNames.join("|");
792
- return `(?i)^(?:${sortedNames.map((toolName) => toolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})$`;
793
- }
794
- function codexCommandHookTrustedHash(params) {
795
- const identity = {
796
- event_name: CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[params.event],
797
- ...params.matcher ? { matcher: params.matcher } : {},
798
- hooks: [{
799
- async: false,
800
- command: params.command,
801
- statusMessage: params.statusMessage,
802
- timeout: params.timeout,
803
- type: "command"
804
- }]
805
- };
806
- return `sha256:${createHash("sha256").update(JSON.stringify(sortJsonValue(identity))).digest("hex")}`;
807
- }
808
- function sortJsonValue(value) {
809
- if (!value || typeof value !== "object") return value;
810
- if (Array.isArray(value)) return value.map(sortJsonValue);
811
- const sorted = {};
812
- for (const [key, entry] of Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right))) sorted[key] = sortJsonValue(entry);
813
- return sorted;
814
- }
815
- //#endregion
816
37
  //#region extensions/codex/src/app-server/event-projector-native-tool-lifecycle.ts
817
38
  /** Projects metadata-only lifecycle diagnostics for native tool items. */
818
39
  var CodexNativeToolLifecycleProjector = class {
@@ -1357,7 +578,7 @@ function createExecAliasDynamicTool(execTool, params) {
1357
578
  description,
1358
579
  parameters: hideExecDynamicToolParameters(execTool.parameters, !nodeAlias || Boolean(pinnedNode), nodeAlias),
1359
580
  execute: async (toolCallId, args, signal, onUpdate) => {
1360
- const result = await execTool.execute(toolCallId, pinExecDynamicToolArgs(args, params.host, pinnedNode), signal, onUpdate);
581
+ const result = await execTool.execute(toolCallId, pinExecDynamicToolArgs(args, params, pinnedNode), signal, onUpdate);
1361
582
  return {
1362
583
  ...result,
1363
584
  content: result.content.map((item) => item.type === "text" ? Object.assign({}, item, { text: item.text.replace(PROCESS_FOLLOWUP_TEXT, followupText) }) : item)
@@ -1372,17 +593,18 @@ function createGatewayProcessAliasDynamicTool(processTool) {
1372
593
  description: "Manage background shell sessions in the existing per-session OpenClaw process scope: list, poll, log, write, send-keys, submit, paste, kill, clear, or remove. Use for gateway_exec follow-up; use native Codex shell session handling for ordinary local work."
1373
594
  };
1374
595
  }
1375
- function pinExecDynamicToolArgs(args, host, configuredNode) {
596
+ function pinExecDynamicToolArgs(args, params, configuredNode) {
1376
597
  const { host: _host, security: _security, ask: _ask, node: requestedNode, ...rest } = normalizeExecDynamicToolArgs(args);
1377
- if (host === "gateway") return {
598
+ if (params.host === "gateway") return {
1378
599
  ...rest,
1379
- host
600
+ host: params.host,
601
+ ...params.ask ? { ask: params.ask } : {}
1380
602
  };
1381
603
  const nodeArgs = Object.fromEntries(Object.entries(rest).filter(([name]) => CODEX_NODE_EXEC_PARAMETER_NAMES.has(name)));
1382
604
  const node = configuredNode ?? (typeof requestedNode === "string" ? requestedNode.trim() : "");
1383
605
  return {
1384
606
  ...nodeArgs,
1385
- host,
607
+ host: params.host,
1386
608
  ...node ? { node } : {}
1387
609
  };
1388
610
  }
@@ -1430,10 +652,10 @@ const CODEX_NATIVE_SANDBOX_TOOL_REQUIREMENTS = [
1430
652
  "apply_patch"
1431
653
  ];
1432
654
  const CODEX_MEMORY_FLUSH_DYNAMIC_TOOL_ALLOW = /* @__PURE__ */ new Set(["read", "write"]);
1433
- /** Keeps paired-device filesystem and process ownership on its native exec-server. */
655
+ /** Keeps node filesystem and process ownership on its native exec-server. */
1434
656
  function resolveCodexNodePlacementToolConstructionPlan(sandbox, nativeToolSurfaceEnabled) {
1435
657
  if (!isCodexRemoteExecPlacementSandbox(sandbox) || sandbox?.backendId !== "node" || !("placementNodeId" in sandbox) || typeof sandbox.placementNodeId !== "string" || !sandbox.placementNodeId) return;
1436
- if (!nativeToolSurfaceEnabled) throw new Error("Codex paired-device remote execution requires its native exec-server tool surface; adjust the session tool policy and start a fresh attempt.");
658
+ if (!nativeToolSurfaceEnabled) throw new Error("Codex node execution requires its native exec-server tool surface; adjust the session tool policy and start a fresh attempt.");
1437
659
  return {
1438
660
  includeBaseCodingTools: false,
1439
661
  includeShellTools: false,
@@ -1550,13 +772,14 @@ async function buildDynamicTools(input) {
1550
772
  ...buildEmbeddedAttemptToolRunContext(params),
1551
773
  exec: {
1552
774
  ...params.execOverrides,
775
+ ...input.sessionPermissionPolicy ? { mode: input.sessionPermissionPolicy.execMode } : {},
1553
776
  ...resolveCodexNodeExecToolOverrides(nativeExecutionPolicy),
1554
777
  config: params.config,
1555
778
  elevated: params.bashElevated
1556
779
  },
1557
- sessionPermissionPolicy: params.permissionMode && params.sessionRoot ? {
1558
- mode: params.permissionMode,
1559
- root: params.sessionRoot
780
+ sessionPermissionPolicy: input.sessionPermissionPolicy ? {
781
+ mode: input.sessionPermissionPolicy.mode,
782
+ root: input.sessionPermissionPolicy.root
1560
783
  } : void 0,
1561
784
  sandbox: input.sandbox,
1562
785
  ...toolConstructionPlan ? { toolConstructionPlan } : {},
@@ -1595,6 +818,7 @@ async function buildDynamicTools(input) {
1595
818
  resolvedWorkspace: input.resolvedWorkspace
1596
819
  }),
1597
820
  config: params.config,
821
+ githubPublicationAvailable: params.githubPublicationAvailable,
1598
822
  authProfileStore: params.toolAuthProfileStore ?? params.authProfileStore,
1599
823
  abortSignal: input.runAbortController.signal,
1600
824
  emitBeforeToolCallDiagnostics: false,
@@ -1761,7 +985,8 @@ function addGatewayShellDynamicToolsIfAvailable(filteredTools, allTools, input,
1761
985
  const processAliasAvailable = Boolean(processTool && !processExcluded && !existingNames.has("gateway_process"));
1762
986
  const toolsToAppend = [createExecAliasDynamicTool(execTool, {
1763
987
  host: "gateway",
1764
- processAliasAvailable
988
+ processAliasAvailable,
989
+ ...input.sessionPermissionPolicy?.mode === "guarded" ? { ask: "always" } : {}
1765
990
  })];
1766
991
  if (processAliasAvailable && processTool) toolsToAppend.push(createGatewayProcessAliasDynamicTool(processTool));
1767
992
  return [...filteredTools, ...toolsToAppend];
@@ -1986,7 +1211,7 @@ const nodeExecServerTextDecoder = new TextDecoder("utf-8", { fatal: true });
1986
1211
  /** Produces the bounded, redacted terminal failure shared by pending and claimed node leases. */
1987
1212
  function createCodexNodeExecServerDisconnectError(reason, cause) {
1988
1213
  const detail = cause === void 0 ? "" : `: ${truncateUtf16Safe(redactSensitiveText(formatErrorMessage$1(cause), { mode: "tools" }), CODEX_NODE_EXEC_SERVER_MAX_FAILURE_DETAIL_CHARS)}`;
1989
- return /* @__PURE__ */ new Error(`Codex paired execution device disconnected; start a fresh attempt. (${reason}${detail})`);
1214
+ return /* @__PURE__ */ new Error(`Codex execution node disconnected; start a fresh attempt. (${reason}${detail})`);
1990
1215
  }
1991
1216
  /** Relays one authorized, single-use Codex exec-server channel without interpreting its protocol. */
1992
1217
  async function startCodexNodeExecServerRelay(params) {
@@ -2013,7 +1238,7 @@ async function startCodexNodeExecServerRelay(params) {
2013
1238
  if (!closed && !params.lease.closed) params.lease.onDisconnected?.(createCodexNodeExecServerDisconnectError(reason, cause));
2014
1239
  closeBoth(code, reason);
2015
1240
  };
2016
- params.lease.onChannelClosed = ({ failed, error }) => failUnexpectedly(failed ? 1011 : 1001, failed ? "execution device failed" : "execution device disconnected", error);
1241
+ params.lease.onChannelClosed = ({ failed, error }) => failUnexpectedly(failed ? 1011 : 1001, failed ? "execution node failed" : "execution node disconnected", error);
2017
1242
  socket.once("close", () => failUnexpectedly(1001, "execution socket closed"));
2018
1243
  socket.once("error", () => failUnexpectedly(1011, "execution socket failed"));
2019
1244
  let toNode = Promise.resolve();
@@ -2217,7 +1442,7 @@ function createCredentialedCodexNodeHttpRejection(request) {
2217
1442
  id: request.id,
2218
1443
  error: {
2219
1444
  code: -32602,
2220
- message: "Authenticated remote HTTP is unavailable on paired devices; run on Gateway or use an intentionally credential-free endpoint."
1445
+ message: "Authenticated remote HTTP is unavailable on execution nodes; run on Gateway or use an intentionally credential-free endpoint."
2221
1446
  }
2222
1447
  }));
2223
1448
  }
@@ -3012,6 +2237,8 @@ async function httpRequest(execServer, notifications, params) {
3012
2237
  const record = requireObject(params, "http/request params");
3013
2238
  const requestId = requireString(record.requestId, "requestId");
3014
2239
  const url = requireString(record.url, "url");
2240
+ const redirectPolicy = record.redirectPolicy ?? "follow";
2241
+ if (redirectPolicy !== "follow" && redirectPolicy !== "stop") throw new Error("http/request redirectPolicy must be follow or stop");
3015
2242
  assertSandboxHttpRequestTargetAllowed(url);
3016
2243
  const request = {
3017
2244
  method: requireString(record.method, "method"),
@@ -3019,6 +2246,7 @@ async function httpRequest(execServer, notifications, params) {
3019
2246
  headers: readHttpHeaders(record.headers),
3020
2247
  bodyBase64: typeof record.bodyBase64 === "string" ? record.bodyBase64 : void 0,
3021
2248
  timeoutMs: typeof record.timeoutMs === "number" && record.timeoutMs > 0 ? Math.floor(record.timeoutMs) : void 0,
2249
+ redirectPolicy,
3022
2250
  streamResponse: record.streamResponse === true
3023
2251
  };
3024
2252
  if (request.streamResponse) return await runStreamingSandboxHttpRequest(execServer, notifications, requestId, request);
@@ -3315,9 +2543,40 @@ def assert_url_allowed(url):
3315
2543
  PINNED_ADDRESSES[hostname] = sorted(addresses)
3316
2544
 
3317
2545
  class GuardedRedirectHandler(urllib.request.HTTPRedirectHandler):
2546
+ def __init__(self, redirect_policy):
2547
+ self.redirect_policy = redirect_policy
2548
+
3318
2549
  def redirect_request(self, req, fp, code, msg, headers, newurl):
2550
+ if self.redirect_policy == "stop":
2551
+ return None
3319
2552
  assert_url_allowed(newurl)
3320
- return super().redirect_request(req, fp, code, msg, headers, newurl)
2553
+ method = req.get_method()
2554
+ drop_body = code == 303 or (code in (301, 302) and method == "POST")
2555
+ next_headers = dict(req.headers)
2556
+ if drop_body:
2557
+ if method != "HEAD":
2558
+ method = "GET"
2559
+ for name in ("content-type", "content-length", "content-encoding", "transfer-encoding"):
2560
+ next_headers.pop(name.capitalize(), None)
2561
+ redirected = urllib.request.Request(
2562
+ newurl,
2563
+ data=None if drop_body else req.data,
2564
+ headers=next_headers,
2565
+ method=method,
2566
+ origin_req_host=req.origin_req_host,
2567
+ unverifiable=True,
2568
+ )
2569
+ previous = urllib.parse.urlsplit(req.full_url)
2570
+ target = urllib.parse.urlsplit(newurl)
2571
+ previous_port = previous.port or (443 if previous.scheme == "https" else 80)
2572
+ target_port = target.port or (443 if target.scheme == "https" else 80)
2573
+ if (previous.scheme, previous.hostname, previous_port) != (
2574
+ target.scheme, target.hostname, target_port
2575
+ ):
2576
+ # Match Codex's route-aware client: cross-origin hops never inherit secrets.
2577
+ for name in ("authorization", "cookie", "proxy-authorization", "www-authenticate", "cookie2"):
2578
+ redirected.remove_header(name.capitalize())
2579
+ return redirected
3321
2580
 
3322
2581
  def pinned_getaddrinfo(original_getaddrinfo):
3323
2582
  def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
@@ -3373,7 +2632,8 @@ def main():
3373
2632
  timeout = None
3374
2633
  if isinstance(timeout_ms, (int, float)) and timeout_ms > 0:
3375
2634
  timeout = timeout_ms / 1000
3376
- opener = urllib.request.build_opener(urllib.request.ProxyHandler({}), GuardedRedirectHandler)
2635
+ redirect_handler = GuardedRedirectHandler(input_data.get("redirectPolicy", "follow"))
2636
+ opener = urllib.request.build_opener(urllib.request.ProxyHandler({}), redirect_handler)
3377
2637
  original_getaddrinfo = socket.getaddrinfo
3378
2638
  socket.getaddrinfo = pinned_getaddrinfo(original_getaddrinfo)
3379
2639
  try:
@@ -3406,6 +2666,7 @@ async function startProcess(execServer, processes, notify, params) {
3406
2666
  const argv = requireStringArray(record.argv, "argv");
3407
2667
  const cwd = resolveExecServerPath(requireString(record.cwd, "cwd"), "process cwd");
3408
2668
  rejectUnsupportedArg0(record.arg0);
2669
+ assertSupportedProcessSandbox(execServer, record);
3409
2670
  const env = readProcessEnv(record);
3410
2671
  const managed = {
3411
2672
  processId,
@@ -3450,7 +2711,18 @@ async function startProcess(execServer, processes, notify, params) {
3450
2711
  } finally {
3451
2712
  if (managed.startPromise === startPromise) managed.startPromise = void 0;
3452
2713
  }
3453
- return { processId };
2714
+ return {
2715
+ processId,
2716
+ sandboxType: "none"
2717
+ };
2718
+ }
2719
+ function assertSupportedProcessSandbox(execServer, record) {
2720
+ if (record.networkProxy !== void 0 && record.networkProxy !== null) throw new Error("Codex sandbox exec-server network proxy launch is not supported.");
2721
+ if (record.enforceManagedNetwork === true || record.managedNetwork !== void 0 && record.managedNetwork !== null) throw new Error("Codex managed network restrictions cannot be enforced by the sandbox backend.");
2722
+ if (resolveFsSandboxPolicy(execServer, record)?.unrestricted === false) throw new Error("Codex process filesystem sandbox restrictions cannot be enforced by the backend.");
2723
+ if (record.sandbox === void 0 || record.sandbox === null) return;
2724
+ if (requireObject(requireObject(record.sandbox, "process sandbox context").permissions, "process sandbox permissions").network !== "restricted") return;
2725
+ if (!execServer.networkIsolated) throw new Error("Codex network restrictions cannot be enforced by the sandbox backend.");
3454
2726
  }
3455
2727
  async function runProcess(execServer, managed, params) {
3456
2728
  const backend = execServer.backend;
@@ -3767,7 +3039,7 @@ async function ensureCodexSandboxExecServerEnvironment(params) {
3767
3039
  if (!params.sandbox?.enabled) return;
3768
3040
  const placementNodeId = readCodexPlacementNodeId(params.sandbox);
3769
3041
  if (!params.sandbox.backend && !placementNodeId) return;
3770
- if (placementNodeId && !params.runtime) throw new Error("Codex paired-device execution requires its active plugin runtime.");
3042
+ if (placementNodeId && !params.runtime) throw new Error("Codex node execution requires its active plugin runtime.");
3771
3043
  if (!canExposeLocalExecServerToAppServer(params.appServerStartOptions)) throw new Error("OpenClaw Codex exec-server uses a local loopback URL and cannot be registered with a remote Codex app-server.");
3772
3044
  const { server: execServer, nodeLease } = await acquireOpenClawExecServer({
3773
3045
  sandbox: params.sandbox,
@@ -3833,7 +3105,7 @@ async function acquireOpenClawExecServer(params) {
3833
3105
  if (!("node" in server)) return { server };
3834
3106
  if (!runtime || !signal) {
3835
3107
  await releaseOpenClawExecServer(server);
3836
- throw new Error("Codex paired-device execution requires an active runtime and attempt.");
3108
+ throw new Error("Codex node execution requires an active runtime and attempt.");
3837
3109
  }
3838
3110
  try {
3839
3111
  const placementIdentity = readCodexPlacementWorkspaceIdentity(sandbox);
@@ -3852,7 +3124,7 @@ async function acquireOpenClawExecServer(params) {
3852
3124
  });
3853
3125
  if (signal.aborted || server.closed || sandboxExecServerRegistry.servers.get(key) !== promise) {
3854
3126
  channel.close();
3855
- throw new Error("Codex paired-device execution retired before its channel was ready.");
3127
+ throw new Error("Codex node execution retired before its channel was ready.");
3856
3128
  }
3857
3129
  const nodeLease = {
3858
3130
  id: randomUUID(),
@@ -3936,7 +3208,8 @@ async function startOpenClawExecServer(sandbox) {
3936
3208
  } : {
3937
3209
  ...common,
3938
3210
  backend: connection.backend,
3939
- fsBridge: connection.fsBridge
3211
+ fsBridge: connection.fsBridge,
3212
+ networkIsolated: (connection.backend.id === "docker" || connection.backend.id === "podman") && sandbox.docker.network.trim().toLowerCase() === "none"
3940
3213
  };
3941
3214
  server.on("connection", (socket, request) => {
3942
3215
  socket.on("error", handleExecServerSocketError);
@@ -3977,7 +3250,7 @@ function readCodexPlacementNodeId(sandbox) {
3977
3250
  return sandbox.placementNodeId;
3978
3251
  }
3979
3252
  function readCodexPlacementWorkspaceIdentity(sandbox) {
3980
- if (!("placementEnvironmentId" in sandbox) || typeof sandbox.placementEnvironmentId !== "string" || !sandbox.placementEnvironmentId || sandbox.placementEnvironmentId.trim() !== sandbox.placementEnvironmentId || !("placementSessionId" in sandbox) || typeof sandbox.placementSessionId !== "string" || !sandbox.placementSessionId || sandbox.placementSessionId.trim() !== sandbox.placementSessionId || !("placementOwnerEpoch" in sandbox) || typeof sandbox.placementOwnerEpoch !== "number" || !Number.isSafeInteger(sandbox.placementOwnerEpoch) || sandbox.placementOwnerEpoch < 1 || !sandbox.sessionKey || sandbox.sessionKey.trim() !== sandbox.sessionKey) throw new Error("Codex paired-device execution requires its exact placement workspace identity.");
3253
+ if (!("placementEnvironmentId" in sandbox) || typeof sandbox.placementEnvironmentId !== "string" || !sandbox.placementEnvironmentId || sandbox.placementEnvironmentId.trim() !== sandbox.placementEnvironmentId || !("placementSessionId" in sandbox) || typeof sandbox.placementSessionId !== "string" || !sandbox.placementSessionId || sandbox.placementSessionId.trim() !== sandbox.placementSessionId || !("placementOwnerEpoch" in sandbox) || typeof sandbox.placementOwnerEpoch !== "number" || !Number.isSafeInteger(sandbox.placementOwnerEpoch) || sandbox.placementOwnerEpoch < 1 || !sandbox.sessionKey || sandbox.sessionKey.trim() !== sandbox.sessionKey) throw new Error("Codex node execution requires its exact placement workspace identity.");
3981
3254
  return {
3982
3255
  environmentId: sandbox.placementEnvironmentId,
3983
3256
  sessionId: sandbox.placementSessionId,
@@ -4018,7 +3291,7 @@ function handleClosedCodexNodeExecServerLease(execServer, lease, result) {
4018
3291
  return;
4019
3292
  }
4020
3293
  try {
4021
- lease.onDisconnected?.(createCodexNodeExecServerDisconnectError(result.failed ? "execution device failed" : "execution device disconnected", result.error));
3294
+ lease.onDisconnected?.(createCodexNodeExecServerDisconnectError(result.failed ? "execution node failed" : "execution node disconnected", result.error));
4022
3295
  } finally {
4023
3296
  closeCodexNodeExecServerLease(execServer, lease);
4024
3297
  }
@@ -4156,7 +3429,7 @@ async function handleCodexAppServerApprovalRequest(params) {
4156
3429
  return buildApprovalResponse(params.method, context.requestParams, resolvedOutcome);
4157
3430
  };
4158
3431
  try {
4159
- if (params.method === "item/commandExecution/requestApproval") {
3432
+ if (params.method === "item/commandExecution/requestApproval" && !readNetworkApprovalContext(requestParams)) {
4160
3433
  const command = readPolicyCommand(requestParams);
4161
3434
  const cwd = readStringField(requestParams, "cwd") ?? params.paramsForRun.workspaceDir;
4162
3435
  const prepareMutableFileApproval = params.paramsForRun.hostCapabilities.prepareMutableFileApproval;
@@ -4183,7 +3456,7 @@ async function handleCodexAppServerApprovalRequest(params) {
4183
3456
  return await resolvePolicyApproval("denied", policyOutcome.reason);
4184
3457
  }
4185
3458
  if (policyOutcome?.outcome === "approved-once" || policyOutcome?.outcome === "approved-session") return await resolvePolicyApproval(policyOutcome.outcome);
4186
- if (CONCRETE_TOOL_AUTO_APPROVAL_METHODS.has(params.method) && params.autoApprove === true) return await resolvePolicyApproval("approved-session", "Codex app-server approval auto-approved by runtime policy.");
3459
+ if (CONCRETE_TOOL_AUTO_APPROVAL_METHODS.has(params.method) && !readNetworkApprovalContext(requestParams) && params.autoApprove === true) return await resolvePolicyApproval("approved-session", "Codex app-server approval auto-approved by runtime policy.");
4187
3460
  const requestResult = await requestPluginApproval({
4188
3461
  hostCapabilities: params.paramsForRun.hostCapabilities,
4189
3462
  title: context.title,
@@ -4299,18 +3572,19 @@ function buildApprovalContext(params) {
4299
3572
  const reasonPreview = sanitizeApprovalPreview(readStringPreview(params.requestParams, "reason"), 180);
4300
3573
  const command = commandPreview.text;
4301
3574
  const reason = reasonPreview.text;
3575
+ const networkApproval = params.method === "item/commandExecution/requestApproval" ? readNetworkApprovalContext(params.requestParams) : void 0;
4302
3576
  const approvalKind = params.method.includes("commandExecution") ? "command" : params.method.includes("fileChange") ? "file-change" : params.method.includes("permissions") ? "permissions" : "other";
4303
3577
  const kind = approvalKind === "command" ? "exec" : approvalKind === "other" ? "unknown" : "plugin";
4304
3578
  const permissionLines = params.method === "item/permissions/requestApproval" ? describeRequestedPermissions(params.requestParams) : [];
4305
- const title = kind === "exec" ? "Codex app-server command approval" : params.method === "item/permissions/requestApproval" ? "Codex app-server permission approval" : kind === "plugin" ? "Codex app-server file approval" : "Codex app-server approval";
4306
- const subject = permissionLines[0] ?? (command ? `Command: ${formatApprovalPreviewSubject(command, commandPreview.omitted)}` : commandPreview.omitted ? `Command: ${APPROVAL_PREVIEW_OMITTED}` : reason ? `Reason: ${formatApprovalPreviewSubject(reason, reasonPreview.omitted)}` : reasonPreview.omitted ? `Reason: ${APPROVAL_PREVIEW_OMITTED}` : `Request method: ${params.method}`);
3579
+ const title = networkApproval ? "Codex app-server network approval" : kind === "exec" ? "Codex app-server command approval" : params.method === "item/permissions/requestApproval" ? "Codex app-server permission approval" : kind === "plugin" ? "Codex app-server file approval" : "Codex app-server approval";
3580
+ const subject = (networkApproval ? `Network: ${sanitizePermissionScalar(networkApproval.protocol)}://${sanitizePermissionHostValue(networkApproval.host)}` : void 0) ?? permissionLines[0] ?? (command ? `Command: ${formatApprovalPreviewSubject(command, commandPreview.omitted)}` : commandPreview.omitted ? `Command: ${APPROVAL_PREVIEW_OMITTED}` : reason ? `Reason: ${formatApprovalPreviewSubject(reason, reasonPreview.omitted)}` : reasonPreview.omitted ? `Reason: ${APPROVAL_PREVIEW_OMITTED}` : `Request method: ${params.method}`);
4307
3581
  return {
4308
3582
  approvalKind,
4309
3583
  kind,
4310
3584
  title,
4311
3585
  description: permissionLines.length > 0 ? joinDescriptionLinesWithinLimit(permissionLines, PERMISSION_DESCRIPTION_MAX_LENGTH) : [subject, ...commandDetailLines].join("\n"),
4312
3586
  severity: kind === "exec" ? "warning" : "info",
4313
- toolName: kind === "exec" ? "codex_command_approval" : params.method === "item/permissions/requestApproval" ? "codex_permission_approval" : "codex_file_approval",
3587
+ toolName: networkApproval ? "codex_network_approval" : kind === "exec" ? "codex_command_approval" : params.method === "item/permissions/requestApproval" ? "codex_permission_approval" : "codex_file_approval",
4314
3588
  itemId,
4315
3589
  approvalId,
4316
3590
  requestParams: params.requestParams,
@@ -4482,6 +3756,10 @@ function sanitizeRelayDecisionReason(value) {
4482
3756
  }
4483
3757
  function buildOpenClawToolPolicyRequest(method, requestParams) {
4484
3758
  if (method === "item/commandExecution/requestApproval") {
3759
+ if (readNetworkApprovalContext(requestParams)) return {
3760
+ toolName: "codex_network_approval",
3761
+ params: { approval: requestParams ?? {} }
3762
+ };
4485
3763
  const command = readPolicyCommand(requestParams);
4486
3764
  return {
4487
3765
  toolName: "exec",
@@ -4769,6 +4047,15 @@ function readPolicyCommand(record) {
4769
4047
  const actionCommands = readCommandActions(record);
4770
4048
  if (actionCommands.length > 0) return actionCommands.join(" && ");
4771
4049
  }
4050
+ function readNetworkApprovalContext(record) {
4051
+ const context = isJsonObject(record?.networkApprovalContext) ? record.networkApprovalContext : void 0;
4052
+ const host = readStringField(context, "host");
4053
+ const protocol = readStringField(context, "protocol");
4054
+ return host && protocol ? {
4055
+ host,
4056
+ protocol
4057
+ } : void 0;
4058
+ }
4772
4059
  function readCommandActions(record) {
4773
4060
  const actions = record?.commandActions;
4774
4061
  if (!Array.isArray(actions)) return [];
@@ -4958,6 +4245,7 @@ async function routeCodexAppServerElicitationRequest(params) {
4958
4245
  if (!requestParams || readNonBlankStringField(requestParams, "threadId") !== params.threadId) return { kind: "not-mine" };
4959
4246
  const requestTurnId = requestParams.turnId;
4960
4247
  if (requestTurnId !== null && requestTurnId !== void 0 && requestTurnId !== params.turnId) return { kind: "not-mine" };
4248
+ if (!((isJsonObject(requestParams["_meta"]) ? requestParams["_meta"] : void 0)?.[MCP_TOOL_APPROVAL_KIND_KEY] === MCP_TOOL_APPROVAL_KIND || params.computerUseMcpServerName !== void 0 && readNonBlankStringField(requestParams, "serverName") === params.computerUseMcpServerName)) return { kind: "not-mine" };
4961
4249
  const pluginResolution = resolvePluginElicitation({
4962
4250
  requestParams,
4963
4251
  pluginAppPolicyContext: params.pluginAppPolicyContext
@@ -4983,7 +4271,7 @@ async function routeCodexAppServerElicitationRequest(params) {
4983
4271
  }));
4984
4272
  }
4985
4273
  const approvalPrompt = readComputerUseApprovalElicitation(requestParams, params.computerUseMcpServerName) ?? readBridgeableApprovalElicitation(requestParams);
4986
- if (!approvalPrompt) return (isJsonObject(requestParams["_meta"]) ? requestParams["_meta"] : void 0)?.[MCP_TOOL_APPROVAL_KIND_KEY] === MCP_TOOL_APPROVAL_KIND || params.computerUseMcpServerName !== void 0 && readNonBlankStringField(requestParams, "serverName") === params.computerUseMcpServerName ? handled(createCodexElicitationResponse("decline")) : { kind: "not-mine" };
4274
+ if (!approvalPrompt) return handled(createCodexElicitationResponse("decline"));
4987
4275
  return handled(buildElicitationResponse(approvalPrompt, await requestPluginApprovalOutcome({
4988
4276
  paramsForRun: params.paramsForRun,
4989
4277
  title: approvalPrompt.title,
@@ -6526,4 +5814,4 @@ function isCronAddAction(args) {
6526
5814
  return typeof action === "string" && action.trim().toLowerCase() === "add";
6527
5815
  }
6528
5816
  //#endregion
6529
- export { buildCodexNativeHookRelayConfig as A, isMatchingDynamicToolTerminalDiagnostic as B, shouldRequireCodexSandboxExecServerEnvironment as C, CodexNativeToolLifecycleProjector as D, readBoundedCodexRemoteWorkspaceFile as E, resolveCodexNativeHookRelayTtlMs as F, toCodexDynamicToolProgressResponse as G, resolveTerminalDynamicToolBatchAction as H, scheduleCodexNativeHookRelayUnregister as I, toCodexDynamicToolProtocolResponse as K, handleDynamicToolCallWithTimeout as L, createCodexNativeHookRelay as M, emitCodexNativePreToolUseFailureDiagnostic as N, CODEX_NATIVE_HOOK_RELAY_EVENTS as O, resolveCodexNativeHookRelayEvents as P, hasPendingDynamicToolTerminalDiagnostic as R, shouldEnableCodexAppServerNativeToolSurface as S, filterCodexVisionTools as T, shouldBlockTerminalReleaseForNonTerminalDynamicToolResult as U, resolveDynamicToolCallTimeoutMs as V, shouldReleaseTurnAfterTerminalDynamicTool as W, resolveCodexAppServerHookChannelId as _, emitDynamicToolStartedDiagnostic as a, resolveCodexNodePlacementToolConstructionPlan as b, resolveCodexProviderWebSearchSupport as c, releaseCodexSandboxExecServerEnvironment as d, buildDynamicTools as f, resolveCodexAppServerExecutionCwd as g, formatCodexDynamicToolBuildStageSummary as h, emitDynamicToolErrorDiagnostic as i, buildCodexNativeHookRelayDisabledConfig as j, CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS as k, resolveCodexProviderWebSearchSupportForClient as l, disableCodexPluginThreadConfig as m, projectCodexExecutableDynamicTools as n, emitDynamicToolTerminalDiagnostic as o, createCodexDynamicToolBuildStageTracker as p, resolveCodexToolAbortTerminalReason as q, routeCodexAppServerElicitationRequest as r, handleCodexAppServerApprovalRequest as s, createCodexDynamicToolBridge as t, ensureCodexSandboxExecServerEnvironment as u, resolveCodexExternalSandboxPolicyForOpenClawSandbox as v, shouldWarnCodexDynamicToolBuildStageSummary as w, resolveCodexSandboxEnvironmentSelection as x, resolveCodexMessageToolProvider as y, isDynamicToolTerminalDiagnosticEvent as z };
5817
+ export { shouldRequireCodexSandboxExecServerEnvironment as C, CodexNativeToolLifecycleProjector as D, readBoundedCodexRemoteWorkspaceFile as E, shouldEnableCodexAppServerNativeToolSurface as S, filterCodexVisionTools as T, resolveCodexAppServerHookChannelId as _, emitDynamicToolStartedDiagnostic as a, resolveCodexNodePlacementToolConstructionPlan as b, resolveCodexProviderWebSearchSupport as c, releaseCodexSandboxExecServerEnvironment as d, buildDynamicTools as f, resolveCodexAppServerExecutionCwd as g, formatCodexDynamicToolBuildStageSummary as h, emitDynamicToolErrorDiagnostic as i, resolveCodexProviderWebSearchSupportForClient as l, disableCodexPluginThreadConfig as m, projectCodexExecutableDynamicTools as n, emitDynamicToolTerminalDiagnostic as o, createCodexDynamicToolBuildStageTracker as p, routeCodexAppServerElicitationRequest as r, handleCodexAppServerApprovalRequest as s, createCodexDynamicToolBridge as t, ensureCodexSandboxExecServerEnvironment as u, resolveCodexExternalSandboxPolicyForOpenClawSandbox as v, shouldWarnCodexDynamicToolBuildStageSummary as w, resolveCodexSandboxEnvironmentSelection as x, resolveCodexMessageToolProvider as y };