@openclaw/codex 2026.8.1-beta.3 → 2026.8.2

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 (69) hide show
  1. package/dist/api.js +5 -6
  2. package/dist/{attempt-client-cleanup-URjUlCHg.js → attempt-client-cleanup-CpVfwctT.js} +47 -5
  3. package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
  4. package/dist/{binding-connection-DAvZwp96.js → binding-connection-CF7BmrDk.js} +2 -2
  5. package/dist/{bounded-turn-4BjrZEwW.js → bounded-turn-DRC3hcjm.js} +16 -20
  6. package/dist/{capabilities-CH3K57v4.js → capabilities-D6m_TdVy.js} +1 -1
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-D0nZ3qtd.js → command-formatters--UToCokS.js} +4 -3
  9. package/dist/{command-handlers-DC86lgT4.js → command-handlers-CLFx2Ypa.js} +114 -79
  10. package/dist/{compact-COqgBRny.js → compact-5xqW1oCh.js} +13 -12
  11. package/dist/{config-CpNeo5AE.js → config-CcYL77O6.js} +350 -288
  12. package/dist/{conversation-binding-data-CW2GuupV.js → conversation-binding-data-BiodpIzq.js} +2 -1
  13. package/dist/doctor-contract-api.js +121 -16
  14. package/dist/{dynamic-tools-fJZhl4CE.js → dynamic-tools-BmeXjwNZ.js} +243 -1002
  15. package/dist/{effective-mcp-catalog-4YKn51aG.js → effective-mcp-catalog-XXQKgK1U.js} +2 -2
  16. package/dist/{event-projector-assistant-message-BwxV4d6N.js → event-projector-assistant-message-C5-Ds8mA.js} +18 -1
  17. package/dist/{event-projector-usage-CLwNEJqW.js → event-projector-usage-B2r5vhWH.js} +9 -14
  18. package/dist/{harness-DoKQcF8n.js → harness-BgeB7LnD.js} +42 -52
  19. package/dist/harness.js +1 -1
  20. package/dist/index.js +308 -306
  21. package/dist/{isolated-completion-DPMtlH9r.js → isolated-completion-CxTus3vZ.js} +4 -4
  22. package/dist/launch-args-sXOAg0SX.js +84 -0
  23. package/dist/{managed-binary-By4FZC6i.js → managed-binary-ZkGodW19.js} +43 -7
  24. package/dist/media-understanding-provider.js +1 -1
  25. package/dist/model-catalog-DULBcniB.js +101 -0
  26. package/dist/{models-CZw1wClT.js → models-DaWfETQk.js} +27 -29
  27. package/dist/{native-subagent-monitor-GGq3RnOo.js → native-subagent-monitor-vXYUZH37.js} +226 -129
  28. package/dist/{node-exec-server.runtime-Bsu1LdFZ.js → node-exec-server.runtime-DirKVZ5h.js} +9 -6
  29. package/dist/{plugin-approval-roundtrip-C75Yiiwc.js → plugin-approval-roundtrip-CCWQGlAI.js} +2 -1
  30. package/dist/{plugin-marketplace-discovery-CSWDkMrP.js → plugin-marketplace-discovery-BXjIFEiw.js} +38 -46
  31. package/dist/{plugin-metadata-cache-BXH5NF-4.js → plugin-metadata-cache-DQQm4gWT.js} +163 -136
  32. package/dist/{protocol-validators-VAVyEnDp.js → protocol-validators-Bg5rpNxk.js} +148 -5
  33. package/dist/{rate-limits-BI1dMHDG.js → rate-limits-BqHEHXQJ.js} +3 -2
  34. package/dist/{sandbox-guard-C1qEeZ5C.js → request-BWxweEdK.js} +183 -2
  35. package/dist/{run-attempt-BV3HzuCf.js → run-attempt-BVLE4Gbi.js} +706 -517
  36. package/dist/{runtime-artifact-BvyT1uyd.js → runtime-artifact-D9pYp4zL.js} +3 -3
  37. package/dist/{session-binding-BTrLf9Qm.js → session-binding-D0zinCP1.js} +195 -122
  38. package/dist/{session-catalog-ezP_UcRM.js → session-catalog-eMhVm0Ql.js} +448 -313
  39. package/dist/session-catalog-home-id-Dt2GiG5o.js +112 -0
  40. package/dist/{session-cli-B1hKvfkI.js → session-cli-B2lyjJA0.js} +1 -1
  41. package/dist/session-history-C5J1AKzG.js +87 -0
  42. package/dist/session-permission-policy-ScnAyG90.js +160 -0
  43. package/dist/session-retirement-CtUtUTr9.js +90 -0
  44. package/dist/{session-upstream-marker-D_2npHlA.js → session-upstream-marker-gqrYIRHk.js} +2 -3
  45. package/dist/settled-turn-context-Bk3IUwod.js +290 -0
  46. package/dist/settled-turn-finalizer-BXLN9PfJ.js +104 -0
  47. package/dist/{shared-client-Dw-rzii0.js → shared-client-B1tT5U6r.js} +1738 -806
  48. package/dist/{side-question-DbXkJZzg.js → side-question-CZ-mCw-t.js} +76 -43
  49. package/dist/{thread-lifecycle-DKdrnTMX.js → thread-lifecycle-CGGKP720.js} +991 -455
  50. package/dist/{thread-ownership-i2NBwDUL.js → thread-ownership-RkaoZCmQ.js} +18 -8
  51. package/dist/{plugin-app-cache-key-DMNQ8Zi8.js → thread-resume-BFt1-ZDa.js} +55 -8
  52. package/dist/{transcript-mirror-BU0_LKAL.js → transcript-mirror-1ep4XjzF.js} +124 -18
  53. package/dist/transport-stdio-NT_Dpd9V.js +724 -0
  54. package/dist/{upstream-prompt-provenance-CFmsKwr9.js → upstream-prompt-provenance-LphB8slG.js} +8 -4
  55. package/dist/{upstream-session-fork-BDVJCLAD.js → upstream-session-fork-Dc8BFExw.js} +76 -90
  56. package/dist/{usage-9dKJCgcy.js → usage-DUhd58gI.js} +3 -3
  57. package/dist/{web-search-provider.runtime-DCdx4DGu.js → web-search-provider.runtime-BEmlE7ov.js} +3 -3
  58. package/openclaw.plugin.json +19 -1
  59. package/package.json +8 -8
  60. package/dist/app-server-policy-bg5QPSVP.js +0 -25
  61. package/dist/command-rpc-xUIIonsO.js +0 -90
  62. package/dist/incognito-session-ET_ufDH-.js +0 -2
  63. package/dist/model-catalog-CmGoqVKB.js +0 -48
  64. package/dist/request-DSK0W6YI.js +0 -157
  65. package/dist/session-catalog-home-id-BNwAJ9D7.js +0 -18
  66. package/dist/session-retirement-CE4iMiLh.js +0 -36
  67. package/dist/settled-turn-finalizer-Dx1onIqg.js +0 -328
  68. package/dist/thread-resume-CdJk2Ehf.js +0 -30
  69. package/dist/transport-stdio-DhoGy33Z.js +0 -99
@@ -1,818 +1,40 @@
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 { C as isCodexRemoteExecPlacementSandbox, T as readCodexPluginConfig } from "./config-CcYL77O6.js";
2
+ import { Bt as CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE, Ht as isJsonObject, dt as resolveCodexToolAbortTerminalReason, et as createCodexElicitationResponse, ft as createFailedDynamicToolResponse, h as releaseLeasedSharedCodexAppServerClient, mt as withDynamicToolTranscriptDetails, pt as withDynamicToolExecutionState } from "./shared-client-B1tT5U6r.js";
3
+ import { d as readCodexTurn } from "./protocol-validators-Bg5rpNxk.js";
4
+ import { D as itemStatus, I as readItem, b as auditNativeToolTerminalStatus, g as isCodexNotificationForTurn, x as auditNativeToolUnfinishedStatus, y as auditNativeToolName } from "./attempt-client-cleanup-CpVfwctT.js";
5
+ import { H as normalizeCodexDynamicToolName, L as filterCodexDynamicTools, R as filterCodexDynamicToolsForDisabledNativeSurface, V as isSystemAgentOnlyCodexDynamicToolAllowlist, d as emitCodexNativePreToolUseFailureDiagnostic, x as resolveCodexWebSearchPlan, z as isForcedPrivateQaCodexRuntime } from "./thread-lifecycle-CGGKP720.js";
6
+ import { l as resolveCodexNativeExecutionPolicy, u as resolveCodexNodeExecToolOverrides } from "./request-BWxweEdK.js";
6
7
  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";
8
+ import { r as formatCodexDisplayText } from "./command-formatters--UToCokS.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-CCWQGlAI.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
+ import { once } from "node:events";
22
23
  import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
23
24
  import { DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS, estimateToolResultTextChars, resolveLiveToolResultMaxChars, sliceToolResultTextToBudget, sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
24
- import { once } from "node:events";
25
+ import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
25
26
  import { isIP } from "node:net";
26
27
  import { WebSocketServer } from "ws";
27
28
  import { fileURLToPath, pathToFileURL } from "node:url";
28
29
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
29
30
  import { redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
30
31
  import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
31
- import { runWithCronCreatorAuthorityCapabilityResolver } from "openclaw/plugin-sdk/codex-mcp-projection";
32
- import { emitTrustedDiagnosticEvent, hasPendingInternalDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
33
32
  import { buildRemoteCommand, isToolAllowed, sanitizeEnvVars } from "openclaw/plugin-sdk/sandbox";
33
+ import { pinExecToolTarget, resolveCodexScheduledToolProjectionFactory, runWithCronCreatorAuthorityCapabilityResolver } from "openclaw/plugin-sdk/codex-mcp-projection";
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";
36
+ import { getCoreTtsToolResultMediaUrls } from "openclaw/plugin-sdk/agent-harness-tool-runtime";
37
37
  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
38
  //#region extensions/codex/src/app-server/event-projector-native-tool-lifecycle.ts
817
39
  /** Projects metadata-only lifecycle diagnostics for native tool items. */
818
40
  var CodexNativeToolLifecycleProjector = class {
@@ -1057,10 +279,10 @@ function joinRemoteWorkspacePath(remoteRoot, suffix) {
1057
279
  //#endregion
1058
280
  //#region extensions/codex/src/app-server/remote-workspace-media.ts
1059
281
  const REMOTE_WORKSPACE_MEDIA_TIMEOUT_MS = 6e4;
1060
- const REMOTE_WORKSPACE_MEDIA_MAX_BYTES = 64 * 1024 * 1024;
282
+ const REMOTE_WORKSPACE_MEDIA_MAX_BYTES = 67108864;
1061
283
  const REMOTE_WORKSPACE_MEDIA_MAX_ATTACHMENTS = 16;
1062
- const CODEX_REMOTE_MEDIA_CHUNK_BYTES = 512 * 1024;
1063
- const CODEX_REMOTE_COMMAND_DEFAULT_OUTPUT_BYTES = 1024 * 1024;
284
+ const CODEX_REMOTE_MEDIA_CHUNK_BYTES = 524288;
285
+ const CODEX_REMOTE_COMMAND_DEFAULT_OUTPUT_BYTES = 1048576;
1064
286
  const CODEX_BOUNDED_REMOTE_FILE_READER = [
1065
287
  "try{",
1066
288
  "const fs=require(\"node:fs\");",
@@ -1326,83 +548,47 @@ const dynamicToolBuildState = {};
1326
548
  const CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME = "node_exec";
1327
549
  const CODEX_GATEWAY_EXEC_DYNAMIC_TOOL_NAME = "gateway_exec";
1328
550
  const CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME = "gateway_process";
1329
- const CODEX_EXEC_POLICY_PARAMETER_NAMES = /* @__PURE__ */ new Set([
1330
- "host",
1331
- "security",
1332
- "ask"
1333
- ]);
1334
- const CODEX_NODE_EXEC_PARAMETER_NAMES = /* @__PURE__ */ new Set([
1335
- "command",
1336
- "workdir",
1337
- "env",
1338
- "timeoutSeconds",
1339
- "node"
1340
- ]);
1341
551
  const PROCESS_FOLLOWUP_TEXT = "Use process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.";
1342
552
  /** Returns true when plugin config explicitly removes any named dynamic tool. */
1343
553
  function isCodexDynamicToolExcluded(config, names) {
1344
554
  const normalizedNames = new Set(names.map((name) => normalizeCodexDynamicToolName(name)));
1345
555
  return (config.codexDynamicToolsExclude ?? []).some((name) => normalizedNames.has(normalizeCodexDynamicToolName(name)));
1346
556
  }
1347
- function createExecAliasDynamicTool(execTool, params) {
1348
- const pinnedNode = params.host === "node" ? params.node?.trim() : void 0;
1349
- const nodeAlias = params.host === "node";
1350
- const gatewayProcessAliasAvailable = params.host === "gateway" && params.processAliasAvailable;
1351
- const name = nodeAlias ? CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME : CODEX_GATEWAY_EXEC_DYNAMIC_TOOL_NAME;
1352
- const description = nodeAlias ? pinnedNode ? "Run a shell command to completion on the OpenClaw configured remote node for this session. This tool always uses OpenClaw host=node internally and follows the existing node exec approval and allowlist policy. Remote-node background follow-up is unavailable. Use Codex's native shell for local app-server work." : "Run a shell command to completion on an OpenClaw remote node. Select the node by name or id when multiple nodes are available. This tool always uses OpenClaw host=node internally and follows the existing node exec approval and allowlist policy. Remote-node background follow-up is unavailable. Use Codex's native shell for local app-server work." : "Run a shell command through OpenClaw on the Gateway host for OpenClaw-managed Gateway environment access, including Secret Store agent-readable environment values and protected egress sentinels. Native Codex shell remains preferred for ordinary local work. This tool always uses OpenClaw host=gateway internally and follows Gateway exec approval and allowlist policy.";
1353
- const followupText = nodeAlias ? "Remote-node background follow-up is unavailable. Wait for the command to complete." : gatewayProcessAliasAvailable ? "Use gateway_process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up." : "Background session follow-up is unavailable because gateway_process is not exposed. Rerun without background=true and set yieldMs high enough to wait for completion.";
1354
- return {
1355
- ...execTool,
1356
- name,
1357
- description,
1358
- parameters: hideExecDynamicToolParameters(execTool.parameters, !nodeAlias || Boolean(pinnedNode), nodeAlias),
1359
- execute: async (toolCallId, args, signal, onUpdate) => {
1360
- const result = await execTool.execute(toolCallId, pinExecDynamicToolArgs(args, params.host, pinnedNode), signal, onUpdate);
1361
- return {
1362
- ...result,
1363
- content: result.content.map((item) => item.type === "text" ? Object.assign({}, item, { text: item.text.replace(PROCESS_FOLLOWUP_TEXT, followupText) }) : item)
1364
- };
1365
- }
557
+ function createNodeExecAliasDynamicTool(execTool, node) {
558
+ const pinnedNode = node?.trim();
559
+ const pinnedTool = pinExecToolTarget(execTool, {
560
+ host: "node",
561
+ ...pinnedNode ? { node: pinnedNode } : {}
562
+ });
563
+ const execute = async (toolCallId, args, signal, onUpdate) => {
564
+ const result = await pinnedTool.execute(toolCallId, args, signal, onUpdate);
565
+ return {
566
+ ...result,
567
+ content: result.content.map((item) => item.type === "text" ? Object.assign({}, item, { text: item.text.replace(PROCESS_FOLLOWUP_TEXT, "Remote-node background follow-up is unavailable. Wait for the command to complete.") }) : item)
568
+ };
1366
569
  };
1367
- }
1368
- function createGatewayProcessAliasDynamicTool(processTool) {
1369
570
  return {
1370
- ...processTool,
1371
- name: CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME,
1372
- 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."
571
+ ...pinnedTool,
572
+ name: CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME,
573
+ description: pinnedNode ? "Run a shell command to completion on the OpenClaw configured remote node for this session. This tool always uses OpenClaw host=node internally and follows the existing node exec approval and allowlist policy. Remote-node background follow-up is unavailable. Use Codex's native shell for local app-server work." : "Run a shell command to completion on an OpenClaw remote node. Select the node by name or id when multiple nodes are available. This tool always uses OpenClaw host=node internally and follows the existing node exec approval and allowlist policy. Remote-node background follow-up is unavailable. Use Codex's native shell for local app-server work.",
574
+ execute
1373
575
  };
1374
576
  }
1375
- function pinExecDynamicToolArgs(args, host, configuredNode) {
1376
- const { host: _host, security: _security, ask: _ask, node: requestedNode, ...rest } = normalizeExecDynamicToolArgs(args);
1377
- if (host === "gateway") return {
1378
- ...rest,
1379
- host
1380
- };
1381
- const nodeArgs = Object.fromEntries(Object.entries(rest).filter(([name]) => CODEX_NODE_EXEC_PARAMETER_NAMES.has(name)));
1382
- const node = configuredNode ?? (typeof requestedNode === "string" ? requestedNode.trim() : "");
1383
- return {
1384
- ...nodeArgs,
1385
- host,
1386
- ...node ? { node } : {}
1387
- };
577
+ function createGatewayExecProjection(createProjection, execTool, params) {
578
+ return createProjection(execTool, {
579
+ kind: "exec",
580
+ name: CODEX_GATEWAY_EXEC_DYNAMIC_TOOL_NAME,
581
+ description: "Run a shell command through OpenClaw on the Gateway host for OpenClaw-managed Gateway environment access, including Secret Store agent-readable environment values and protected egress sentinels. Native Codex shell remains preferred for ordinary local work. This tool always uses OpenClaw host=gateway internally and follows Gateway exec approval and allowlist policy.",
582
+ followupText: params.processAliasAvailable ? "Use gateway_process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up." : "Background session follow-up is unavailable because gateway_process is not exposed. Rerun without background=true and set yieldMs high enough to wait for completion.",
583
+ ...params.ask ? { ask: params.ask } : {}
584
+ });
1388
585
  }
1389
- function normalizeExecDynamicToolArgs(args) {
1390
- return args && typeof args === "object" && !Array.isArray(args) ? args : {};
1391
- }
1392
- function hideExecDynamicToolParameters(parameters, hideNode, nodeOnly) {
1393
- if (!parameters || typeof parameters !== "object" || Array.isArray(parameters)) return parameters;
1394
- const schema = parameters;
1395
- const rawProperties = schema.properties;
1396
- if (!rawProperties || typeof rawProperties !== "object" || Array.isArray(rawProperties)) return parameters;
1397
- const includeParameter = (name) => nodeOnly ? CODEX_NODE_EXEC_PARAMETER_NAMES.has(name) && !(hideNode && name === "node") : !CODEX_EXEC_POLICY_PARAMETER_NAMES.has(normalizeCodexDynamicToolName(name)) && !(hideNode && normalizeCodexDynamicToolName(name) === "node");
1398
- const nextProperties = Object.fromEntries(Object.entries(rawProperties).filter(([name]) => includeParameter(name)));
1399
- const rawRequired = schema.required;
1400
- const nextRequired = Array.isArray(rawRequired) ? rawRequired.filter((name) => typeof name !== "string" || includeParameter(name)) : rawRequired;
1401
- return {
1402
- ...schema,
1403
- properties: nextProperties,
1404
- ...Array.isArray(rawRequired) ? { required: nextRequired } : {}
1405
- };
586
+ function createGatewayProcessProjection(createProjection, processTool) {
587
+ return createProjection(processTool, {
588
+ kind: "process",
589
+ name: CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME,
590
+ 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."
591
+ });
1406
592
  }
1407
593
  //#endregion
1408
594
  //#region extensions/codex/src/app-server/vision-tools.ts
@@ -1430,10 +616,10 @@ const CODEX_NATIVE_SANDBOX_TOOL_REQUIREMENTS = [
1430
616
  "apply_patch"
1431
617
  ];
1432
618
  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. */
619
+ /** Keeps node filesystem and process ownership on its native exec-server. */
1434
620
  function resolveCodexNodePlacementToolConstructionPlan(sandbox, nativeToolSurfaceEnabled) {
1435
621
  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.");
622
+ 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
623
  return {
1438
624
  includeBaseCodingTools: false,
1439
625
  includeShellTools: false,
@@ -1447,13 +633,6 @@ function preserveRingZeroSystemAgentTool(allTools, filteredTools) {
1447
633
  if (!openclaw) return filteredTools;
1448
634
  return [openclaw, ...filteredTools.filter((tool) => tool.name !== "openclaw")];
1449
635
  }
1450
- /** Splits sandbox and run session keys so tool calls can bind to both scopes when needed. */
1451
- function resolveOpenClawCodingToolsSessionKeys(params, sandboxSessionKey) {
1452
- return {
1453
- sessionKey: sandboxSessionKey,
1454
- runSessionKey: params.sessionKey && params.sessionKey !== sandboxSessionKey ? params.sessionKey : void 0
1455
- };
1456
- }
1457
636
  /** Returns the canonical channel used for Codex message routing and receipts. */
1458
637
  function resolveCodexMessageToolProvider(params) {
1459
638
  return params.messageChannel ?? params.messageProvider;
@@ -1518,6 +697,10 @@ async function buildDynamicTools(input) {
1518
697
  ...params,
1519
698
  disableMessageTool: false
1520
699
  } : params;
700
+ const toolRunContext = buildEmbeddedAttemptToolRunContext({
701
+ ...params,
702
+ forceMessageTool: shouldForceMessageTool(messagePolicyParams)
703
+ });
1521
704
  if (params.disableTools) {
1522
705
  input.onWebSearchPolicyResolved?.(false);
1523
706
  return [];
@@ -1531,11 +714,11 @@ async function buildDynamicTools(input) {
1531
714
  const modelHasVision = params.model.input?.includes("image") ?? false;
1532
715
  const agentDir = params.agentDir ?? resolveAgentDir$1(params.config ?? {}, input.sessionAgentId);
1533
716
  const injectedOpenClawCodingToolsFactory = dynamicToolBuildState.openClawCodingToolsFactory;
1534
- let agentHarnessModule;
1535
- const loadAgentHarnessModule = async () => agentHarnessModule ??= await import("openclaw/plugin-sdk/agent-harness");
1536
- toolBuildStages.mark("load-agent-harness-tools");
1537
- const sessionKeys = resolveOpenClawCodingToolsSessionKeys(params, input.sandboxSessionKey);
1538
- const nativeExecutionPolicy = resolveCodexNativeExecutionPolicyForDynamicTools(input);
717
+ const nativeExecutionPolicy = resolveCodexNativeExecutionPolicyForRun(params, {
718
+ agentId: input.policyAgentId,
719
+ runtimeSessionKey: input.sandboxSessionKey,
720
+ sandbox: input.sandbox
721
+ });
1539
722
  const webSearchPlan = resolveCodexWebSearchPlan({
1540
723
  config: params.config,
1541
724
  disableTools: params.disableTools,
@@ -1547,16 +730,18 @@ async function buildDynamicTools(input) {
1547
730
  const toolConstructionPlan = resolveCodexNodePlacementToolConstructionPlan(input.sandbox, input.nativeToolSurfaceEnabled);
1548
731
  const options = {
1549
732
  agentId: input.sessionAgentId,
1550
- ...buildEmbeddedAttemptToolRunContext(params),
733
+ policyAgentId: input.policyAgentId,
734
+ ...toolRunContext,
1551
735
  exec: {
1552
736
  ...params.execOverrides,
737
+ ...input.sessionPermissionPolicy ? { mode: input.sessionPermissionPolicy.execMode } : {},
1553
738
  ...resolveCodexNodeExecToolOverrides(nativeExecutionPolicy),
1554
739
  config: params.config,
1555
740
  elevated: params.bashElevated
1556
741
  },
1557
- sessionPermissionPolicy: params.permissionMode && params.sessionRoot ? {
1558
- mode: params.permissionMode,
1559
- root: params.sessionRoot
742
+ sessionPermissionPolicy: input.sessionPermissionPolicy ? {
743
+ mode: input.sessionPermissionPolicy.mode,
744
+ root: input.sessionPermissionPolicy.root
1560
745
  } : void 0,
1561
746
  sandbox: input.sandbox,
1562
747
  ...toolConstructionPlan ? { toolConstructionPlan } : {},
@@ -1582,7 +767,8 @@ async function buildDynamicTools(input) {
1582
767
  trustedInternalHandoff: params.trustedInternalHandoff,
1583
768
  scheduledToolPolicy: params.scheduledToolPolicy,
1584
769
  allowGatewaySubagentBinding: params.allowGatewaySubagentBinding || isForcedPrivateQaCodexRuntime(),
1585
- ...sessionKeys,
770
+ sessionKey: input.sandboxSessionKey,
771
+ runSessionKey: params.sessionKey && params.sessionKey !== input.sandboxSessionKey ? params.sessionKey : void 0,
1586
772
  sessionId: params.sessionId,
1587
773
  runId: params.runId,
1588
774
  approvalReviewerDeviceId: params.approvalReviewerDeviceId,
@@ -1595,6 +781,7 @@ async function buildDynamicTools(input) {
1595
781
  resolvedWorkspace: input.resolvedWorkspace
1596
782
  }),
1597
783
  config: params.config,
784
+ githubPublicationAvailable: params.githubPublicationAvailable,
1598
785
  authProfileStore: params.toolAuthProfileStore ?? params.authProfileStore,
1599
786
  abortSignal: input.runAbortController.signal,
1600
787
  emitBeforeToolCallDiagnostics: false,
@@ -1616,6 +803,7 @@ async function buildDynamicTools(input) {
1616
803
  hasRepliedRef: params.hasRepliedRef,
1617
804
  modelHasVision,
1618
805
  computerContextEpoch: input.computerContextEpoch,
806
+ registerRunCleanup: input.registerRunCleanup,
1619
807
  requireExplicitMessageTarget: params.requireExplicitMessageTarget ?? isSubagentSessionKey(params.sessionKey),
1620
808
  sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
1621
809
  taskSuggestionDeliveryMode: params.taskSuggestionDeliveryMode,
@@ -1663,7 +851,7 @@ async function buildDynamicTools(input) {
1663
851
  const readableAllTools = [...readableAllToolProjection.tools];
1664
852
  const normallyProfiledTools = input.nativeToolSurfaceEnabled === false ? filterCodexDynamicToolsForDisabledNativeSurface(readableAllTools, input.pluginConfig, { preserveShell: shouldKeepOpenClawShellDynamicTools(input, nativeExecutionPolicy) }) : filterCodexDynamicTools(readableAllTools, input.pluginConfig);
1665
853
  const profileFilteredTools = (input.isHostScopedToolActive?.("openclaw") ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow) ? preserveRingZeroSystemAgentTool(readableAllTools, normallyProfiledTools) : normallyProfiledTools;
1666
- const codexFilteredTools = addNodeShellDynamicToolsIfNeeded(addGatewayShellDynamicToolsIfAvailable(addSandboxShellDynamicToolsIfAvailable(isCodexMemoryFlushRun(params) ? filterCodexMemoryFlushDynamicTools(readableAllTools) : profileFilteredTools, readableAllTools, input), readableAllTools, input, nativeExecutionPolicy), readableAllTools, input, nativeExecutionPolicy);
854
+ const codexFilteredTools = addNodeShellDynamicToolsIfNeeded(addGatewayShellDynamicToolsIfAvailable(addSandboxShellDynamicToolsIfAvailable(isCodexMemoryFlushRun(params) ? filterCodexMemoryFlushDynamicTools(readableAllTools) : profileFilteredTools, readableAllTools, input, nativeExecutionPolicy), readableAllTools, input, nativeExecutionPolicy), readableAllTools, input, nativeExecutionPolicy);
1667
855
  toolBuildStages.mark("codex-filtering");
1668
856
  const visionFilteredTools = filterCodexVisionTools(codexFilteredTools, {
1669
857
  modelHasVision,
@@ -1672,33 +860,33 @@ async function buildDynamicTools(input) {
1672
860
  toolBuildStages.mark("vision-filtering");
1673
861
  const webSearchPresent = visionFilteredTools.some((tool) => tool.name === "web_search");
1674
862
  const persistentCodexWebSearchSurface = params.config?.tools?.web?.search?.enabled !== false && !(input.pluginConfig.codexDynamicToolsExclude ?? []).some((name) => normalizeCodexDynamicToolName(name) === "web_search");
1675
- const webSearchPolicy = webSearchPresent || persistentCodexWebSearchSurface ? (await loadAgentHarnessModule()).resolveWebSearchToolPolicy({
1676
- config: params.config,
1677
- modelProvider: params.model.provider,
1678
- modelId: params.modelId,
1679
- agentId: input.sessionAgentId,
1680
- sessionKey: input.sandboxSessionKey,
1681
- sandboxToolPolicy: input.sandbox?.tools,
1682
- messageProvider: resolveCodexMessageToolProvider(params),
1683
- agentAccountId: params.agentAccountId,
1684
- groupId: params.groupId,
1685
- groupChannel: params.groupChannel,
1686
- groupSpace: params.groupSpace,
1687
- spawnedBy: params.spawnedBy,
1688
- senderId: params.senderId,
1689
- senderName: params.senderName,
1690
- senderUsername: params.senderUsername,
1691
- senderE164: params.senderE164,
1692
- inputProvenance: params.inputProvenance,
1693
- trustedInternalHandoff: params.trustedInternalHandoff,
1694
- scheduledToolPolicy: params.scheduledToolPolicy
1695
- }) : {
1696
- allowed: false,
1697
- persistentAllowed: false
1698
- };
1699
- const transientWebSearchRestriction = !webSearchPolicy.allowed && webSearchPolicy.persistentAllowed || isCodexMemoryFlushRun(params);
1700
- input.onPersistentWebSearchPolicyResolved?.(webSearchPresent || persistentCodexWebSearchSurface && transientWebSearchRestriction && webSearchPolicy.persistentAllowed);
1701
- const filteredTools = filterCodexDynamicToolsForAllowlist(visionFilteredTools, includeForcedCodexDynamicToolAllow(params.toolsAllow, messagePolicyParams));
863
+ let persistentWebSearchAllowed = webSearchPresent;
864
+ if (input.onPersistentWebSearchPolicyResolved && !webSearchPresent && persistentCodexWebSearchSurface) {
865
+ const webSearchPolicy = (await import("openclaw/plugin-sdk/agent-harness")).resolveWebSearchToolPolicy({
866
+ config: params.config,
867
+ modelProvider: params.model.provider,
868
+ modelId: params.modelId,
869
+ agentId: input.policyAgentId,
870
+ sessionKey: input.sandboxSessionKey,
871
+ sandboxToolPolicy: input.sandbox?.tools,
872
+ messageProvider: resolveCodexMessageToolProvider(params),
873
+ agentAccountId: params.agentAccountId,
874
+ groupId: params.groupId,
875
+ groupChannel: params.groupChannel,
876
+ groupSpace: params.groupSpace,
877
+ spawnedBy: params.spawnedBy,
878
+ senderId: params.senderId,
879
+ senderName: params.senderName,
880
+ senderUsername: params.senderUsername,
881
+ senderE164: params.senderE164,
882
+ inputProvenance: params.inputProvenance,
883
+ trustedInternalHandoff: params.trustedInternalHandoff,
884
+ scheduledToolPolicy: params.scheduledToolPolicy
885
+ });
886
+ persistentWebSearchAllowed = webSearchPolicy.persistentAllowed && (!webSearchPolicy.allowed || isCodexMemoryFlushRun(params));
887
+ }
888
+ input.onPersistentWebSearchPolicyResolved?.(persistentWebSearchAllowed);
889
+ const filteredTools = filterCodexDynamicToolsForAllowlist(visionFilteredTools, toolRunContext.runtimeToolAllowlist);
1702
890
  toolBuildStages.mark("allowlist-filter");
1703
891
  const normalizedTools = normalizeAgentRuntimeTools({
1704
892
  runtimePlan: input.ignoreRuntimePlan ? void 0 : params.runtimePlan,
@@ -1759,48 +947,39 @@ function addGatewayShellDynamicToolsIfAvailable(filteredTools, allTools, input,
1759
947
  if (!execTool || execExcluded || existingNames.has("gateway_exec")) return filteredTools;
1760
948
  const processExcluded = isCodexDynamicToolExcluded(input.pluginConfig, ["process", CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME]);
1761
949
  const processAliasAvailable = Boolean(processTool && !processExcluded && !existingNames.has("gateway_process"));
1762
- const toolsToAppend = [createExecAliasDynamicTool(execTool, {
1763
- host: "gateway",
1764
- processAliasAvailable
950
+ const createProjection = resolveCodexScheduledToolProjectionFactory(input.params.hostCapabilities);
951
+ if (!createProjection) return filteredTools;
952
+ const toolsToAppend = [createGatewayExecProjection(createProjection, execTool, {
953
+ processAliasAvailable,
954
+ ...input.sessionPermissionPolicy?.mode === "guarded" ? { ask: "always" } : {}
1765
955
  })];
1766
- if (processAliasAvailable && processTool) toolsToAppend.push(createGatewayProcessAliasDynamicTool(processTool));
956
+ if (processAliasAvailable && processTool) toolsToAppend.push(createGatewayProcessProjection(createProjection, processTool));
1767
957
  return [...filteredTools, ...toolsToAppend];
1768
958
  }
1769
- /** Preserves delivery-critical tools when a narrow allowlist would otherwise hide them. */
1770
- function includeForcedCodexDynamicToolAllow(toolsAllow, params) {
1771
- if (toolsAllow === void 0 || hasWildcardCodexToolsAllow(toolsAllow)) return toolsAllow;
1772
- const forcedToolNames = shouldForceMessageTool(params) ? ["message"] : [];
1773
- if (forcedToolNames.length === 0) return toolsAllow;
1774
- if (toolsAllow.length === 0) return forcedToolNames;
1775
- const normalized = new Set(toolsAllow.map((name) => normalizeCodexDynamicToolName(name)));
1776
- const missingToolNames = forcedToolNames.filter((toolName) => !normalized.has(normalizeCodexDynamicToolName(toolName)));
1777
- return missingToolNames.length === 0 ? toolsAllow : [...toolsAllow, ...missingToolNames];
1778
- }
1779
959
  /** Decides whether Codex native code mode can own shell/file tools for this turn. */
1780
960
  function shouldEnableCodexAppServerNativeToolSurface(params, sandbox, options = {}) {
1781
961
  if (params.pluginHarnessToolPolicyRestricted === true) return false;
1782
962
  if (isCodexMemoryFlushRun(params)) return false;
1783
963
  if (params.disableTools) return false;
1784
- if (isCodexNativeExecutionBlockedByNodeExecHost(params, {
964
+ if (!resolveCodexNativeExecutionPolicyForRun(params, {
1785
965
  agentId: options.agentId,
1786
966
  runtimeSessionKey: options.runtimeSessionKey,
1787
967
  sandbox
1788
- })) return false;
1789
- const toolsAllow = includeForcedCodexDynamicToolAllow(params.toolsAllow, params);
968
+ }).nativeToolSurfaceAllowed) return false;
969
+ const toolsAllow = params.toolsAllow;
1790
970
  if (toolsAllow === void 0) return canCodexAppServerNativeToolSurfaceHonorSandbox(sandbox, options);
1791
971
  return hasWildcardCodexToolsAllow(toolsAllow) && canCodexAppServerNativeToolSurfaceHonorSandbox(sandbox, options);
1792
972
  }
1793
- /** Returns true when OpenClaw policy requires the Node-owned exec/process tools instead. */
1794
- function isCodexNativeExecutionBlockedByNodeExecHost(params, options = {}) {
1795
- return !resolveCodexNativeExecutionPolicy({
973
+ function resolveCodexNativeExecutionPolicyForRun(params, options = {}) {
974
+ return resolveCodexNativeExecutionPolicy({
1796
975
  config: params.config,
1797
976
  sessionKey: resolveCodexRuntimePolicySessionKey(params, options.runtimeSessionKey),
1798
977
  sessionId: params.sessionId,
1799
978
  agentId: options.agentId,
1800
979
  execOverrides: params.execOverrides,
1801
- sandboxAvailable: options.sandbox?.enabled,
980
+ sandboxAvailable: options.sandbox === null ? false : options.sandbox?.enabled,
1802
981
  readRuntimeSessionEntry: true
1803
- }).nativeToolSurfaceAllowed;
982
+ });
1804
983
  }
1805
984
  function resolveCodexRuntimePolicySessionKey(params, runtimeSessionKey) {
1806
985
  return runtimeSessionKey?.trim() || params.sandboxSessionKey?.trim() || params.sessionKey?.trim() || params.sessionId;
@@ -1863,8 +1042,8 @@ function disableCodexPluginThreadConfig(pluginConfig) {
1863
1042
  };
1864
1043
  }
1865
1044
  /** Adds sandbox_exec/process aliases when native Code Mode cannot directly honor the sandbox. */
1866
- function addSandboxShellDynamicToolsIfAvailable(filteredTools, allTools, input) {
1867
- if (!shouldExposeSandboxExecDynamicTool(input) || isSandboxShellDynamicToolExcluded(input.pluginConfig)) return filteredTools;
1045
+ function addSandboxShellDynamicToolsIfAvailable(filteredTools, allTools, input, executionPolicy) {
1046
+ if (isCodexMemoryFlushRun(input.params) || !executionPolicy.nativeToolSurfaceAllowed || !input.sandbox?.enabled || !input.sandbox.backendId.trim() || input.nativeToolSurfaceEnabled !== false || isSandboxShellDynamicToolExcluded(input.pluginConfig)) return filteredTools;
1868
1047
  const execTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "exec");
1869
1048
  const processTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "process");
1870
1049
  if (!execTool || !processTool) return filteredTools;
@@ -1891,16 +1070,6 @@ function addSandboxShellDynamicToolsIfAvailable(filteredTools, allTools, input)
1891
1070
  sandboxProcessTool
1892
1071
  ];
1893
1072
  }
1894
- function shouldExposeSandboxExecDynamicTool(input) {
1895
- if (isCodexMemoryFlushRun(input.params)) return false;
1896
- if (isCodexNativeExecutionBlockedByNodeExecHost(input.params, {
1897
- agentId: input.sessionAgentId,
1898
- runtimeSessionKey: input.sandboxSessionKey,
1899
- sandbox: input.sandbox
1900
- })) return false;
1901
- const backendId = input.sandbox?.enabled ? input.sandbox.backendId.trim().toLowerCase() : "";
1902
- return Boolean(backendId && input.nativeToolSurfaceEnabled === false);
1903
- }
1904
1073
  function isSandboxShellDynamicToolExcluded(config) {
1905
1074
  return isCodexDynamicToolExcluded(config, [
1906
1075
  "exec",
@@ -1916,26 +1085,12 @@ function addNodeShellDynamicToolsIfNeeded(filteredTools, allTools, input, nodePo
1916
1085
  if (!nodeExecIsDefault && !nodeExecAvailableFromAuto) return filteredTools;
1917
1086
  const execTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "exec");
1918
1087
  if (!execTool) return filteredTools;
1919
- if (!isCodexDynamicToolExcluded(input.pluginConfig, ["exec", "node_exec"]) && !filteredTools.some((tool) => normalizeCodexDynamicToolName(tool.name) === "node_exec")) return [...filteredTools, createExecAliasDynamicTool(execTool, {
1920
- host: "node",
1921
- node: nodePolicy.node
1922
- })];
1088
+ if (!isCodexDynamicToolExcluded(input.pluginConfig, ["exec", "node_exec"]) && !filteredTools.some((tool) => normalizeCodexDynamicToolName(tool.name) === "node_exec")) return [...filteredTools, createNodeExecAliasDynamicTool(execTool, nodePolicy.node)];
1923
1089
  return filteredTools;
1924
1090
  }
1925
1091
  function shouldKeepOpenClawShellDynamicTools(input, nodePolicy) {
1926
1092
  return !isCodexMemoryFlushRun(input.params) && input.nativeToolSurfaceEnabled === false && input.sandbox?.enabled !== true && nodePolicy.effectiveExecHost !== "node";
1927
1093
  }
1928
- function resolveCodexNativeExecutionPolicyForDynamicTools(input) {
1929
- return resolveCodexNativeExecutionPolicy({
1930
- config: input.params.config,
1931
- sessionKey: resolveCodexRuntimePolicySessionKey(input.params, input.sandboxSessionKey),
1932
- sessionId: input.params.sessionId,
1933
- agentId: input.sessionAgentId,
1934
- execOverrides: input.params.execOverrides,
1935
- sandboxAvailable: input.sandbox?.enabled,
1936
- readRuntimeSessionEntry: true
1937
- });
1938
- }
1939
1094
  /** Applies a normalized tool allowlist while preserving shell aliases for exec/process. */
1940
1095
  function filterCodexDynamicToolsForAllowlist(tools, toolsAllow) {
1941
1096
  if (!toolsAllow) return tools;
@@ -1957,9 +1112,9 @@ function shouldForceMessageTool(params) {
1957
1112
  }
1958
1113
  //#endregion
1959
1114
  //#region extensions/codex/src/app-server/sandbox-exec-server-node-relay.ts
1960
- const CODEX_NODE_EXEC_SERVER_MAX_MESSAGE_BYTES$1 = 64 * 1024 * 1024;
1115
+ const CODEX_NODE_EXEC_SERVER_MAX_MESSAGE_BYTES$1 = 67108864;
1961
1116
  const CODEX_NODE_EXEC_SERVER_MAX_FAILURE_DETAIL_CHARS = 240;
1962
- const CODEX_NODE_HTTP_CREDENTIAL_BODY_MAX_BYTES = 1024 * 1024;
1117
+ const CODEX_NODE_HTTP_CREDENTIAL_BODY_MAX_BYTES = 1048576;
1963
1118
  const CODEX_NODE_HTTP_CREDENTIAL_SCAN_MAX_FIELDS = 256;
1964
1119
  const CODEX_NODE_HTTP_CREDENTIAL_SCAN_MAX_DEPTH = 8;
1965
1120
  const CODEX_NODE_HTTP_CREDENTIAL_HEADER_NAMES = /* @__PURE__ */ new Set([
@@ -1986,7 +1141,7 @@ const nodeExecServerTextDecoder = new TextDecoder("utf-8", { fatal: true });
1986
1141
  /** Produces the bounded, redacted terminal failure shared by pending and claimed node leases. */
1987
1142
  function createCodexNodeExecServerDisconnectError(reason, cause) {
1988
1143
  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})`);
1144
+ return /* @__PURE__ */ new Error(`Codex execution node disconnected; start a fresh attempt. (${reason}${detail})`);
1990
1145
  }
1991
1146
  /** Relays one authorized, single-use Codex exec-server channel without interpreting its protocol. */
1992
1147
  async function startCodexNodeExecServerRelay(params) {
@@ -2013,7 +1168,7 @@ async function startCodexNodeExecServerRelay(params) {
2013
1168
  if (!closed && !params.lease.closed) params.lease.onDisconnected?.(createCodexNodeExecServerDisconnectError(reason, cause));
2014
1169
  closeBoth(code, reason);
2015
1170
  };
2016
- params.lease.onChannelClosed = ({ failed, error }) => failUnexpectedly(failed ? 1011 : 1001, failed ? "execution device failed" : "execution device disconnected", error);
1171
+ params.lease.onChannelClosed = ({ failed, error }) => failUnexpectedly(failed ? 1011 : 1001, failed ? "execution node failed" : "execution node disconnected", error);
2017
1172
  socket.once("close", () => failUnexpectedly(1001, "execution socket closed"));
2018
1173
  socket.once("error", () => failUnexpectedly(1011, "execution socket failed"));
2019
1174
  let toNode = Promise.resolve();
@@ -2217,7 +1372,7 @@ function createCredentialedCodexNodeHttpRejection(request) {
2217
1372
  id: request.id,
2218
1373
  error: {
2219
1374
  code: -32602,
2220
- message: "Authenticated remote HTTP is unavailable on paired devices; run on Gateway or use an intentionally credential-free endpoint."
1375
+ message: "Authenticated remote HTTP is unavailable on execution nodes; run on Gateway or use an intentionally credential-free endpoint."
2221
1376
  }
2222
1377
  }));
2223
1378
  }
@@ -2599,10 +1754,10 @@ function joinSandboxChildPath(parent, child) {
2599
1754
  * Implements filesystem JSON-RPC handlers for the Codex sandbox exec-server
2600
1755
  * with OpenClaw sandbox policy checks before every bridge operation.
2601
1756
  */
2602
- const CODEX_SANDBOX_EXEC_SERVER_MAX_READ_FILE_BYTES = 512 * 1024 * 1024;
1757
+ const CODEX_SANDBOX_EXEC_SERVER_MAX_READ_FILE_BYTES = 536870912;
2603
1758
  const CODEX_SANDBOX_EXEC_SERVER_MAX_OPEN_FILE_READS = 128;
2604
- const CODEX_SANDBOX_EXEC_SERVER_MAX_BUFFERED_FILE_READ_BYTES = 64 * 1024 * 1024;
2605
- const CODEX_SANDBOX_EXEC_SERVER_MAX_READ_BLOCK_BYTES = 1024 * 1024;
1759
+ const CODEX_SANDBOX_EXEC_SERVER_MAX_BUFFERED_FILE_READ_BYTES = 67108864;
1760
+ const CODEX_SANDBOX_EXEC_SERVER_MAX_READ_BLOCK_BYTES = 1048576;
2606
1761
  const CODEX_SANDBOX_EXEC_SERVER_MAX_FILE_READ_HANDLE_ID_BYTES = 32;
2607
1762
  /** Opens a policy-checked sandbox file under a bounded, connection-owned handle. */
2608
1763
  async function openFile(execServer, handles, params) {
@@ -3006,12 +2161,14 @@ function delay(ms) {
3006
2161
  * access through the active OpenClaw sandbox backend.
3007
2162
  */
3008
2163
  /** Maximum JSON-line size accepted from the streaming HTTP helper process. */
3009
- const SANDBOX_HTTP_STREAM_LINE_MAX_CHARS = 256 * 1024;
2164
+ const SANDBOX_HTTP_STREAM_LINE_MAX_CHARS = 262144;
3010
2165
  /** Handles one sandbox HTTP JSON-RPC request, optionally streaming response body deltas. */
3011
2166
  async function httpRequest(execServer, notifications, params) {
3012
2167
  const record = requireObject(params, "http/request params");
3013
2168
  const requestId = requireString(record.requestId, "requestId");
3014
2169
  const url = requireString(record.url, "url");
2170
+ const redirectPolicy = record.redirectPolicy ?? "follow";
2171
+ if (redirectPolicy !== "follow" && redirectPolicy !== "stop") throw new Error("http/request redirectPolicy must be follow or stop");
3015
2172
  assertSandboxHttpRequestTargetAllowed(url);
3016
2173
  const request = {
3017
2174
  method: requireString(record.method, "method"),
@@ -3019,6 +2176,7 @@ async function httpRequest(execServer, notifications, params) {
3019
2176
  headers: readHttpHeaders(record.headers),
3020
2177
  bodyBase64: typeof record.bodyBase64 === "string" ? record.bodyBase64 : void 0,
3021
2178
  timeoutMs: typeof record.timeoutMs === "number" && record.timeoutMs > 0 ? Math.floor(record.timeoutMs) : void 0,
2179
+ redirectPolicy,
3022
2180
  streamResponse: record.streamResponse === true
3023
2181
  };
3024
2182
  if (request.streamResponse) return await runStreamingSandboxHttpRequest(execServer, notifications, requestId, request);
@@ -3315,9 +2473,43 @@ def assert_url_allowed(url):
3315
2473
  PINNED_ADDRESSES[hostname] = sorted(addresses)
3316
2474
 
3317
2475
  class GuardedRedirectHandler(urllib.request.HTTPRedirectHandler):
2476
+ # Python 3.9 lacks the 308 dispatch alias; use the same guarded redirect path.
2477
+ http_error_308 = urllib.request.HTTPRedirectHandler.http_error_302
2478
+
2479
+ def __init__(self, redirect_policy):
2480
+ self.redirect_policy = redirect_policy
2481
+
3318
2482
  def redirect_request(self, req, fp, code, msg, headers, newurl):
2483
+ if self.redirect_policy == "stop":
2484
+ return None
3319
2485
  assert_url_allowed(newurl)
3320
- return super().redirect_request(req, fp, code, msg, headers, newurl)
2486
+ method = req.get_method()
2487
+ drop_body = code == 303 or (code in (301, 302) and method == "POST")
2488
+ next_headers = dict(req.headers)
2489
+ if drop_body:
2490
+ if method != "HEAD":
2491
+ method = "GET"
2492
+ for name in ("content-type", "content-length", "content-encoding", "transfer-encoding"):
2493
+ next_headers.pop(name.capitalize(), None)
2494
+ redirected = urllib.request.Request(
2495
+ newurl,
2496
+ data=None if drop_body else req.data,
2497
+ headers=next_headers,
2498
+ method=method,
2499
+ origin_req_host=req.origin_req_host,
2500
+ unverifiable=True,
2501
+ )
2502
+ previous = urllib.parse.urlsplit(req.full_url)
2503
+ target = urllib.parse.urlsplit(newurl)
2504
+ previous_port = previous.port or (443 if previous.scheme == "https" else 80)
2505
+ target_port = target.port or (443 if target.scheme == "https" else 80)
2506
+ if (previous.scheme, previous.hostname, previous_port) != (
2507
+ target.scheme, target.hostname, target_port
2508
+ ):
2509
+ # Match Codex's route-aware client: cross-origin hops never inherit secrets.
2510
+ for name in ("authorization", "cookie", "proxy-authorization", "www-authenticate", "cookie2"):
2511
+ redirected.remove_header(name.capitalize())
2512
+ return redirected
3321
2513
 
3322
2514
  def pinned_getaddrinfo(original_getaddrinfo):
3323
2515
  def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
@@ -3373,7 +2565,8 @@ def main():
3373
2565
  timeout = None
3374
2566
  if isinstance(timeout_ms, (int, float)) and timeout_ms > 0:
3375
2567
  timeout = timeout_ms / 1000
3376
- opener = urllib.request.build_opener(urllib.request.ProxyHandler({}), GuardedRedirectHandler)
2568
+ redirect_handler = GuardedRedirectHandler(input_data.get("redirectPolicy", "follow"))
2569
+ opener = urllib.request.build_opener(urllib.request.ProxyHandler({}), redirect_handler)
3377
2570
  original_getaddrinfo = socket.getaddrinfo
3378
2571
  socket.getaddrinfo = pinned_getaddrinfo(original_getaddrinfo)
3379
2572
  try:
@@ -3396,7 +2589,7 @@ python3 "$tmp"
3396
2589
  * termination for Codex sandbox exec-server process RPCs.
3397
2590
  */
3398
2591
  const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
3399
- const RETAINED_PROCESS_OUTPUT_BYTES = 1024 * 1024;
2592
+ const RETAINED_PROCESS_OUTPUT_BYTES = 1048576;
3400
2593
  const CLOSED_PROCESS_EVICTION_MS = 6e4;
3401
2594
  /** Starts a sandbox-backed process and registers it in the connection-local process table. */
3402
2595
  async function startProcess(execServer, processes, notify, params) {
@@ -3406,6 +2599,7 @@ async function startProcess(execServer, processes, notify, params) {
3406
2599
  const argv = requireStringArray(record.argv, "argv");
3407
2600
  const cwd = resolveExecServerPath(requireString(record.cwd, "cwd"), "process cwd");
3408
2601
  rejectUnsupportedArg0(record.arg0);
2602
+ assertSupportedProcessSandbox(execServer, record);
3409
2603
  const env = readProcessEnv(record);
3410
2604
  const managed = {
3411
2605
  processId,
@@ -3450,7 +2644,18 @@ async function startProcess(execServer, processes, notify, params) {
3450
2644
  } finally {
3451
2645
  if (managed.startPromise === startPromise) managed.startPromise = void 0;
3452
2646
  }
3453
- return { processId };
2647
+ return {
2648
+ processId,
2649
+ sandboxType: "none"
2650
+ };
2651
+ }
2652
+ function assertSupportedProcessSandbox(execServer, record) {
2653
+ if (record.networkProxy !== void 0 && record.networkProxy !== null) throw new Error("Codex sandbox exec-server network proxy launch is not supported.");
2654
+ 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.");
2655
+ if (resolveFsSandboxPolicy(execServer, record)?.unrestricted === false) throw new Error("Codex process filesystem sandbox restrictions cannot be enforced by the backend.");
2656
+ if (record.sandbox === void 0 || record.sandbox === null) return;
2657
+ if (requireObject(requireObject(record.sandbox, "process sandbox context").permissions, "process sandbox permissions").network !== "restricted") return;
2658
+ if (!execServer.networkIsolated) throw new Error("Codex network restrictions cannot be enforced by the sandbox backend.");
3454
2659
  }
3455
2660
  async function runProcess(execServer, managed, params) {
3456
2661
  const backend = execServer.backend;
@@ -3641,10 +2846,11 @@ function readEnv(value) {
3641
2846
  return env;
3642
2847
  }
3643
2848
  function readProcessEnv(record) {
3644
- return sanitizeEnvVars({
2849
+ const requestedEnv = {
3645
2850
  ...buildEnvFromPolicy(record.envPolicy),
3646
2851
  ...readEnv(record.env)
3647
- }).allowed;
2852
+ };
2853
+ return sanitizeEnvVars(requestedEnv).allowed;
3648
2854
  }
3649
2855
  function buildEnvFromPolicy(value) {
3650
2856
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
@@ -3759,15 +2965,15 @@ var CodexSandboxExecSession = class {
3759
2965
  * Hosts the local OpenClaw sandbox exec-server that Codex app-server native
3760
2966
  * execution can register as an external environment.
3761
2967
  */
3762
- const CODEX_SANDBOX_EXEC_SERVER_MAX_INBOUND_MESSAGE_BYTES = 100 * 1024 * 1024;
3763
- const CODEX_NODE_EXEC_SERVER_MAX_MESSAGE_BYTES = 64 * 1024 * 1024;
2968
+ const CODEX_SANDBOX_EXEC_SERVER_MAX_INBOUND_MESSAGE_BYTES = 104857600;
2969
+ const CODEX_NODE_EXEC_SERVER_MAX_MESSAGE_BYTES = 67108864;
3764
2970
  const codexNodeExecServerLeases = /* @__PURE__ */ new WeakMap();
3765
2971
  /** Starts or reuses a sandbox exec-server and registers it with Codex app-server. */
3766
2972
  async function ensureCodexSandboxExecServerEnvironment(params) {
3767
2973
  if (!params.sandbox?.enabled) return;
3768
2974
  const placementNodeId = readCodexPlacementNodeId(params.sandbox);
3769
2975
  if (!params.sandbox.backend && !placementNodeId) return;
3770
- if (placementNodeId && !params.runtime) throw new Error("Codex paired-device execution requires its active plugin runtime.");
2976
+ if (placementNodeId && !params.runtime) throw new Error("Codex node execution requires its active plugin runtime.");
3771
2977
  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
2978
  const { server: execServer, nodeLease } = await acquireOpenClawExecServer({
3773
2979
  sandbox: params.sandbox,
@@ -3833,7 +3039,7 @@ async function acquireOpenClawExecServer(params) {
3833
3039
  if (!("node" in server)) return { server };
3834
3040
  if (!runtime || !signal) {
3835
3041
  await releaseOpenClawExecServer(server);
3836
- throw new Error("Codex paired-device execution requires an active runtime and attempt.");
3042
+ throw new Error("Codex node execution requires an active runtime and attempt.");
3837
3043
  }
3838
3044
  try {
3839
3045
  const placementIdentity = readCodexPlacementWorkspaceIdentity(sandbox);
@@ -3852,7 +3058,7 @@ async function acquireOpenClawExecServer(params) {
3852
3058
  });
3853
3059
  if (signal.aborted || server.closed || sandboxExecServerRegistry.servers.get(key) !== promise) {
3854
3060
  channel.close();
3855
- throw new Error("Codex paired-device execution retired before its channel was ready.");
3061
+ throw new Error("Codex node execution retired before its channel was ready.");
3856
3062
  }
3857
3063
  const nodeLease = {
3858
3064
  id: randomUUID(),
@@ -3936,7 +3142,8 @@ async function startOpenClawExecServer(sandbox) {
3936
3142
  } : {
3937
3143
  ...common,
3938
3144
  backend: connection.backend,
3939
- fsBridge: connection.fsBridge
3145
+ fsBridge: connection.fsBridge,
3146
+ networkIsolated: (connection.backend.id === "docker" || connection.backend.id === "podman") && sandbox.docker.network.trim().toLowerCase() === "none"
3940
3147
  };
3941
3148
  server.on("connection", (socket, request) => {
3942
3149
  socket.on("error", handleExecServerSocketError);
@@ -3977,7 +3184,7 @@ function readCodexPlacementNodeId(sandbox) {
3977
3184
  return sandbox.placementNodeId;
3978
3185
  }
3979
3186
  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.");
3187
+ 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
3188
  return {
3982
3189
  environmentId: sandbox.placementEnvironmentId,
3983
3190
  sessionId: sandbox.placementSessionId,
@@ -4018,7 +3225,7 @@ function handleClosedCodexNodeExecServerLease(execServer, lease, result) {
4018
3225
  return;
4019
3226
  }
4020
3227
  try {
4021
- lease.onDisconnected?.(createCodexNodeExecServerDisconnectError(result.failed ? "execution device failed" : "execution device disconnected", result.error));
3228
+ lease.onDisconnected?.(createCodexNodeExecServerDisconnectError(result.failed ? "execution node failed" : "execution node disconnected", result.error));
4022
3229
  } finally {
4023
3230
  closeCodexNodeExecServerLease(execServer, lease);
4024
3231
  }
@@ -4140,6 +3347,8 @@ async function handleCodexAppServerApprovalRequest(params) {
4140
3347
  resolvedOutcome = "approved-once";
4141
3348
  resolvedMessage = "Codex app-server approval granted for this byte-bound command only.";
4142
3349
  }
3350
+ params.signal?.throwIfAborted();
3351
+ if (resolvedOutcome !== "denied") params.paramsForRun.hostCapabilities.assertActive();
4143
3352
  emitApprovalEvent(params.paramsForRun, {
4144
3353
  phase: "resolved",
4145
3354
  kind: context.kind,
@@ -4156,7 +3365,7 @@ async function handleCodexAppServerApprovalRequest(params) {
4156
3365
  return buildApprovalResponse(params.method, context.requestParams, resolvedOutcome);
4157
3366
  };
4158
3367
  try {
4159
- if (params.method === "item/commandExecution/requestApproval") {
3368
+ if (params.method === "item/commandExecution/requestApproval" && !readNetworkApprovalContext(requestParams)) {
4160
3369
  const command = readPolicyCommand(requestParams);
4161
3370
  const cwd = readStringField(requestParams, "cwd") ?? params.paramsForRun.workspaceDir;
4162
3371
  const prepareMutableFileApproval = params.paramsForRun.hostCapabilities.prepareMutableFileApproval;
@@ -4178,14 +3387,16 @@ async function handleCodexAppServerApprovalRequest(params) {
4178
3387
  autoApprove: params.autoApprove,
4179
3388
  signal: params.signal
4180
3389
  });
3390
+ params.signal?.throwIfAborted();
4181
3391
  if (policyOutcome?.outcome === "denied") {
4182
3392
  recordNativeToolFailureDisposition(params, context, policyOutcome.failureDisposition);
4183
3393
  return await resolvePolicyApproval("denied", policyOutcome.reason);
4184
3394
  }
4185
3395
  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.");
3396
+ 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
3397
  const requestResult = await requestPluginApproval({
4188
3398
  hostCapabilities: params.paramsForRun.hostCapabilities,
3399
+ signal: params.signal,
4189
3400
  title: context.title,
4190
3401
  description: context.description,
4191
3402
  severity: context.severity,
@@ -4299,18 +3510,19 @@ function buildApprovalContext(params) {
4299
3510
  const reasonPreview = sanitizeApprovalPreview(readStringPreview(params.requestParams, "reason"), 180);
4300
3511
  const command = commandPreview.text;
4301
3512
  const reason = reasonPreview.text;
3513
+ const networkApproval = params.method === "item/commandExecution/requestApproval" ? readNetworkApprovalContext(params.requestParams) : void 0;
4302
3514
  const approvalKind = params.method.includes("commandExecution") ? "command" : params.method.includes("fileChange") ? "file-change" : params.method.includes("permissions") ? "permissions" : "other";
4303
3515
  const kind = approvalKind === "command" ? "exec" : approvalKind === "other" ? "unknown" : "plugin";
4304
3516
  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}`);
3517
+ 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";
3518
+ 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
3519
  return {
4308
3520
  approvalKind,
4309
3521
  kind,
4310
3522
  title,
4311
3523
  description: permissionLines.length > 0 ? joinDescriptionLinesWithinLimit(permissionLines, PERMISSION_DESCRIPTION_MAX_LENGTH) : [subject, ...commandDetailLines].join("\n"),
4312
3524
  severity: kind === "exec" ? "warning" : "info",
4313
- toolName: kind === "exec" ? "codex_command_approval" : params.method === "item/permissions/requestApproval" ? "codex_permission_approval" : "codex_file_approval",
3525
+ toolName: networkApproval ? "codex_network_approval" : kind === "exec" ? "codex_command_approval" : params.method === "item/permissions/requestApproval" ? "codex_permission_approval" : "codex_file_approval",
4314
3526
  itemId,
4315
3527
  approvalId,
4316
3528
  requestParams: params.requestParams,
@@ -4482,6 +3694,10 @@ function sanitizeRelayDecisionReason(value) {
4482
3694
  }
4483
3695
  function buildOpenClawToolPolicyRequest(method, requestParams) {
4484
3696
  if (method === "item/commandExecution/requestApproval") {
3697
+ if (readNetworkApprovalContext(requestParams)) return {
3698
+ toolName: "codex_network_approval",
3699
+ params: { approval: requestParams ?? {} }
3700
+ };
4485
3701
  const command = readPolicyCommand(requestParams);
4486
3702
  return {
4487
3703
  toolName: "exec",
@@ -4694,7 +3910,8 @@ function summarizeNetworkPolicyAmendments(value) {
4694
3910
  function sanitizePermissionHostValue(value) {
4695
3911
  const withoutScheme = sanitizePermissionScalar(value).toLowerCase().replace(/^[a-z][a-z0-9+.-]*:\/\//, "");
4696
3912
  const authority = withoutScheme.split(/[/?#]/, 1)[0] ?? withoutScheme;
4697
- return truncateCodexApprovalDisplayText(authority.includes("@") ? authority.slice(authority.lastIndexOf("@") + 1) : authority, PERMISSION_VALUE_MAX_LENGTH);
3913
+ const withoutUserInfo = authority.includes("@") ? authority.slice(authority.lastIndexOf("@") + 1) : authority;
3914
+ return truncateCodexApprovalDisplayText(withoutUserInfo, PERMISSION_VALUE_MAX_LENGTH);
4698
3915
  }
4699
3916
  function sanitizePermissionPathValue(value) {
4700
3917
  return truncateCodexApprovalDisplayText(formatApprovalDisplayPath(sanitizePermissionScalar(value)), PERMISSION_VALUE_MAX_LENGTH);
@@ -4769,6 +3986,15 @@ function readPolicyCommand(record) {
4769
3986
  const actionCommands = readCommandActions(record);
4770
3987
  if (actionCommands.length > 0) return actionCommands.join(" && ");
4771
3988
  }
3989
+ function readNetworkApprovalContext(record) {
3990
+ const context = isJsonObject(record?.networkApprovalContext) ? record.networkApprovalContext : void 0;
3991
+ const host = readStringField(context, "host");
3992
+ const protocol = readStringField(context, "protocol");
3993
+ return host && protocol ? {
3994
+ host,
3995
+ protocol
3996
+ } : void 0;
3997
+ }
4772
3998
  function readCommandActions(record) {
4773
3999
  const actions = record?.commandActions;
4774
4000
  if (!Array.isArray(actions)) return [];
@@ -4814,7 +4040,8 @@ function appendPreviewPart(source, part, separator) {
4814
4040
  }
4815
4041
  function sanitizeApprovalPreview(source, maxLength) {
4816
4042
  if (!source || !source.value) return { omitted: false };
4817
- const sanitized = sanitizeCodexApprovalVisibleText(stripDanglingCodexApprovalTerminalSequence(source.value));
4043
+ const rawPreview = stripDanglingCodexApprovalTerminalSequence(source.value);
4044
+ const sanitized = sanitizeCodexApprovalVisibleText(rawPreview);
4818
4045
  if (!sanitized) return { omitted: true };
4819
4046
  return {
4820
4047
  text: formatCodexDisplayText(truncateCodexApprovalDisplayText(sanitized, maxLength)),
@@ -4958,6 +4185,8 @@ async function routeCodexAppServerElicitationRequest(params) {
4958
4185
  if (!requestParams || readNonBlankStringField(requestParams, "threadId") !== params.threadId) return { kind: "not-mine" };
4959
4186
  const requestTurnId = requestParams.turnId;
4960
4187
  if (requestTurnId !== null && requestTurnId !== void 0 && requestTurnId !== params.turnId) return { kind: "not-mine" };
4188
+ 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" };
4189
+ if (params.signal?.aborted) return handled(createCodexElicitationResponse("cancel"));
4961
4190
  const pluginResolution = resolvePluginElicitation({
4962
4191
  requestParams,
4963
4192
  pluginAppPolicyContext: params.pluginAppPolicyContext
@@ -4983,7 +4212,7 @@ async function routeCodexAppServerElicitationRequest(params) {
4983
4212
  }));
4984
4213
  }
4985
4214
  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" };
4215
+ if (!approvalPrompt) return handled(createCodexElicitationResponse("decline"));
4987
4216
  return handled(buildElicitationResponse(approvalPrompt, await requestPluginApprovalOutcome({
4988
4217
  paramsForRun: params.paramsForRun,
4989
4218
  title: approvalPrompt.title,
@@ -5256,7 +4485,8 @@ function readDisplayParamLines(meta) {
5256
4485
  return remaining > 0 ? [...lines, `- Additional parameters: ${remaining} more`] : lines;
5257
4486
  }
5258
4487
  function formatDisplayParamValue(value) {
5259
- return truncateCodexApprovalDisplayText(sanitizeDisplayText(typeof value === "string" ? value : formatDisplayJsonValue(value ?? null)), MAX_DISPLAY_PARAM_VALUE_LENGTH);
4488
+ const formatted = typeof value === "string" ? value : formatDisplayJsonValue(value ?? null);
4489
+ return truncateCodexApprovalDisplayText(sanitizeDisplayText(formatted), MAX_DISPLAY_PARAM_VALUE_LENGTH);
5260
4490
  }
5261
4491
  function formatDisplayJsonValue(value, depth = MAX_DISPLAY_VALUE_DEPTH) {
5262
4492
  if (value === null) return "null";
@@ -5304,6 +4534,7 @@ async function requestPluginApprovalOutcome(params) {
5304
4534
  try {
5305
4535
  const requestResult = await requestPluginApproval({
5306
4536
  hostCapabilities: params.paramsForRun.hostCapabilities,
4537
+ signal: params.signal,
5307
4538
  title: params.title,
5308
4539
  description: params.description,
5309
4540
  severity: "warning",
@@ -5490,7 +4721,7 @@ function assertCodexDynamicToolInputMatchesSchema(params) {
5490
4721
  const visibleErrors = validation.errors.slice(0, MAX_CODEX_DYNAMIC_TOOL_VALIDATION_ERRORS);
5491
4722
  const details = visibleErrors.map((error) => {
5492
4723
  if (error.text.length <= MAX_CODEX_DYNAMIC_TOOL_VALIDATION_ERROR_CHARS) return error.text;
5493
- return `${error.text.slice(0, MAX_CODEX_DYNAMIC_TOOL_VALIDATION_ERROR_CHARS - 19)}${CODEX_DYNAMIC_TOOL_VALIDATION_TRUNCATED_SUFFIX}`;
4724
+ return `${error.text.slice(0, 141)}${CODEX_DYNAMIC_TOOL_VALIDATION_TRUNCATED_SUFFIX}`;
5494
4725
  }).join("; ");
5495
4726
  const omitted = validation.errors.length - visibleErrors.length;
5496
4727
  const omittedSuffix = omitted > 0 ? `; ${omitted} more violation(s) omitted` : "";
@@ -5699,6 +4930,8 @@ function createCodexDynamicToolBridge(params) {
5699
4930
  messagingToolSentTargets: [],
5700
4931
  messagingToolSourceReplyPayloads: [],
5701
4932
  toolMediaUrls: [],
4933
+ toolAutoDeliveryMediaUrls: [],
4934
+ coreTtsToolResults: [],
5702
4935
  toolAudioAsVoice: false,
5703
4936
  acceptedSessionSpawns: [],
5704
4937
  quarantinedTools
@@ -5912,13 +5145,17 @@ function createCodexDynamicToolBridge(params) {
5912
5145
  const toolConfirmedSourceReply = params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && !resultIsError && (rawResult.terminate === true || result.terminate === true);
5913
5146
  const confirmedSourceReply = params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && (toolConfirmedSourceReply || deliveredSourceReply || receiptConfirmedSourceReply);
5914
5147
  const sourceReplyFinal = confirmedSourceReply ? executedArgs.final !== false : void 0;
5148
+ const autoDeliveryTtsMediaUrls = getCoreTtsToolResultMediaUrls(rawResult);
5915
5149
  collectToolTelemetry({
5916
5150
  toolName,
5917
5151
  args: executedArgs,
5918
5152
  result,
5919
5153
  mediaTrustResult: telemetryRawResult,
5920
5154
  telemetry,
5155
+ signal,
5921
5156
  isError: resultIsError,
5157
+ autoDeliveryTtsMediaUrls,
5158
+ coreTtsToolResult: autoDeliveryTtsMediaUrls?.length ? rawResult : void 0,
5922
5159
  messagingTarget: confirmedMessagingTarget,
5923
5160
  sourceReplyFinal
5924
5161
  });
@@ -5950,13 +5187,6 @@ function createCodexDynamicToolBridge(params) {
5950
5187
  toolCallOrdinal: options?.toolCallOrdinal
5951
5188
  });
5952
5189
  notifyAgentToolResult(options?.onAgentToolResult, toolName, failedResult, true);
5953
- collectToolTelemetry({
5954
- toolName,
5955
- args: executedArgs,
5956
- result: void 0,
5957
- telemetry,
5958
- isError: true
5959
- });
5960
5190
  runAgentHarnessAfterToolCallHook({
5961
5191
  toolName,
5962
5192
  toolCallId: call.callId,
@@ -6281,15 +5511,26 @@ function collectToolTelemetry(params) {
6281
5511
  const response = normalizeHeartbeatToolResponse(params.result?.details);
6282
5512
  if (response) params.telemetry.heartbeatToolResponse = response;
6283
5513
  }
6284
- if (!params.isError && params.result) {
5514
+ if (params.result && !params.signal.aborted) {
6285
5515
  const media = extractToolResultMediaArtifact(params.result);
6286
5516
  if (media) {
6287
5517
  const mediaUrls = filterToolResultMediaUrls(params.toolName, media.mediaUrls, params.mediaTrustResult ?? params.result);
6288
5518
  const seen = new Set(params.telemetry.toolMediaUrls);
6289
- for (const mediaUrl of mediaUrls) if (!seen.has(mediaUrl)) {
6290
- seen.add(mediaUrl);
6291
- params.telemetry.toolMediaUrls.push(mediaUrl);
5519
+ const autoDeliveryMediaUrls = new Set(params.telemetry.toolAutoDeliveryMediaUrls);
5520
+ const rawAutoDeliveryMediaUrls = new Set(params.autoDeliveryTtsMediaUrls);
5521
+ let retainsCoreTtsMedia = false;
5522
+ for (const mediaUrl of mediaUrls) {
5523
+ if (!seen.has(mediaUrl)) {
5524
+ seen.add(mediaUrl);
5525
+ params.telemetry.toolMediaUrls.push(mediaUrl);
5526
+ }
5527
+ if (rawAutoDeliveryMediaUrls.has(mediaUrl)) {
5528
+ autoDeliveryMediaUrls.add(mediaUrl);
5529
+ retainsCoreTtsMedia = true;
5530
+ } else autoDeliveryMediaUrls.delete(mediaUrl);
6292
5531
  }
5532
+ params.telemetry.toolAutoDeliveryMediaUrls = [...autoDeliveryMediaUrls];
5533
+ if (retainsCoreTtsMedia && params.coreTtsToolResult && !params.telemetry.coreTtsToolResults.includes(params.coreTtsToolResult)) params.telemetry.coreTtsToolResults.push(params.coreTtsToolResult);
6293
5534
  if (media.audioAsVoice) params.telemetry.toolAudioAsVoice = true;
6294
5535
  }
6295
5536
  }
@@ -6526,4 +5767,4 @@ function isCronAddAction(args) {
6526
5767
  return typeof action === "string" && action.trim().toLowerCase() === "add";
6527
5768
  }
6528
5769
  //#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 };
5770
+ 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 };