@openclaw/codex 2026.9.1-beta.1 → 2026.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/api.js +5 -6
  2. package/dist/{attempt-client-cleanup-bfsmcQ6m.js → attempt-client-cleanup-DYHcRQAa.js} +31 -45
  3. package/dist/attempt-context-DmQUNWwT.js +634 -0
  4. package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
  5. package/dist/{binding-connection-BMvPX73o.js → binding-connection-E6uDsPlx.js} +2 -2
  6. package/dist/{bounded-turn-B7iBUcuP.js → bounded-turn-wzJrwKKe.js} +15 -20
  7. package/dist/{capabilities-BRMh0aIq.js → capabilities-dIjNVCYH.js} +1 -1
  8. package/dist/cli-metadata.js +1 -1
  9. package/dist/{command-formatters-DEsIufkt.js → command-formatters-CLHhEB1Q.js} +5 -4
  10. package/dist/{command-handlers-D_-1_qRp.js → command-handlers-CTGb2csu.js} +124 -101
  11. package/dist/{compact-BglNFZrQ.js → compact-_t0HQrUS.js} +38 -29
  12. package/dist/{config-giW8X0QA.js → config-BhmfC1s6.js} +294 -282
  13. package/dist/{context-compaction-activity-BOeJf7yo.js → context-compaction-activity-CMla5FPJ.js} +4 -1
  14. package/dist/{conversation-binding-data-CW2GuupV.js → conversation-binding-data-BiodpIzq.js} +2 -1
  15. package/dist/doctor-contract-api.js +143 -12
  16. package/dist/{dynamic-tools-Dnlfa--w.js → dynamic-tools-BU9tmlYm.js} +599 -655
  17. package/dist/{effective-mcp-catalog-CTeGrnfO.js → effective-mcp-catalog-DzYBW4h1.js} +2 -2
  18. package/dist/{event-projector-assistant-message-C5-Ds8mA.js → event-projector-assistant-message-DHRFXKbS.js} +4 -4
  19. package/dist/{event-projector-usage-Bk1I90Bg.js → event-projector-usage-C5mcrGjp.js} +15 -148
  20. package/dist/{harness-Csz0F-DK.js → harness-DXVZ5II5.js} +42 -52
  21. package/dist/harness.js +1 -1
  22. package/dist/index.js +458 -404
  23. package/dist/{isolated-completion-Cv6FqygM.js → isolated-completion-kg9kv4iH.js} +4 -4
  24. package/dist/launch-args-RXQwn8zV.js +90 -0
  25. package/dist/{managed-binary-DFYK4DWq.js → managed-binary-4-Vjz5GH.js} +14 -9
  26. package/dist/media-understanding-provider.js +1 -1
  27. package/dist/model-catalog-CUM68Hkl.js +101 -0
  28. package/dist/{models-TS3HBU-q.js → models-sYnEc0gd.js} +25 -28
  29. package/dist/{native-subagent-monitor-4Mfq2HDs.js → native-subagent-monitor-CnH147jH.js} +55 -115
  30. package/dist/{node-exec-server.runtime-BpWqJO1V.js → node-exec-server.runtime-CstNHCmC.js} +9 -6
  31. package/dist/{plugin-approval-roundtrip-bUm777tR.js → plugin-approval-roundtrip-CNe-Xrjx.js} +30 -3
  32. package/dist/{plugin-marketplace-discovery-B9DvaAG-.js → plugin-marketplace-discovery-CfFlKqDl.js} +15 -14
  33. package/dist/{plugin-metadata-cache-CTCT3w5L.js → plugin-metadata-cache-BFrPhb2c.js} +6 -6
  34. package/dist/provider-capabilities-DW5p4k9w.js +101 -0
  35. package/dist/{rate-limits-C5_D8Gmb.js → rate-limits-CawQJfUS.js} +3 -2
  36. package/dist/{sandbox-guard-kuWTn9C5.js → request-DYAI5FQE.js} +195 -2
  37. package/dist/{run-attempt-DYc0QCBF.js → run-attempt-D1hJO4fX.js} +828 -2100
  38. package/dist/{runtime-artifact-BvyT1uyd.js → runtime-artifact-C3jVimFn.js} +21 -14
  39. package/dist/{session-binding-Bcynfd-_.js → session-binding-BHuTCRZQ.js} +198 -127
  40. package/dist/{session-catalog-DaLKVtpH.js → session-catalog-yyvTWFOw.js} +564 -408
  41. package/dist/{session-cli-DMc4oS2w.js → session-cli-CmFEXMAM.js} +1 -1
  42. package/dist/session-history-C5J1AKzG.js +87 -0
  43. package/dist/session-initialization-BeWzjGiR.js +74 -0
  44. package/dist/{session-permission-policy-BrNH1bjy.js → session-permission-policy-DeWVTCMj.js} +2 -2
  45. package/dist/session-retirement-BujDjXVF.js +97 -0
  46. package/dist/{session-upstream-marker-BIkusb95.js → session-upstream-marker-C351-5Qw.js} +19 -4
  47. package/dist/settled-turn-context-D3SNWtUn.js +268 -0
  48. package/dist/settled-turn-finalizer-BVJx4yoc.js +104 -0
  49. package/dist/{shared-client-DKz23hHY.js → shared-client-DxcfF9KX.js} +3480 -679
  50. package/dist/{side-question-DjA_jsNe.js → side-question-BTZAII4l.js} +108 -70
  51. package/dist/{thread-lifecycle-L7oEkTlq.js → thread-lifecycle-DAcEJ_Om.js} +2188 -1915
  52. package/dist/{thread-ownership-DZeuKH1j.js → thread-ownership-zGokd8i1.js} +25 -14
  53. package/dist/thread-policy-g4KbJPzp.js +54 -0
  54. package/dist/{plugin-app-cache-key-C1CAdfcP.js → thread-resume-C7pyVKeW.js} +59 -7
  55. package/dist/{transcript-mirror-DsCMWBca.js → transcript-mirror-DTUxFWa_.js} +79 -17
  56. package/dist/transport-stdio-CyPldczm.js +726 -0
  57. package/dist/{upstream-prompt-provenance-CFmsKwr9.js → upstream-prompt-provenance-LphB8slG.js} +8 -4
  58. package/dist/upstream-session-fork-CYlBIGVt.js +966 -0
  59. package/dist/{usage-ROIaW3wo.js → usage-Bj-rrzkR.js} +3 -3
  60. package/dist/{web-search-provider.runtime-ClaNPV7N.js → web-search-provider.runtime-D2FWSK4w.js} +3 -3
  61. package/openclaw.plugin.json +1 -30
  62. package/package.json +6 -6
  63. package/dist/command-rpc-CCeCkf_v.js +0 -90
  64. package/dist/incognito-session-ET_ufDH-.js +0 -2
  65. package/dist/model-catalog-i9rNBthX.js +0 -48
  66. package/dist/protocol-validators-VeJxxJ0U.js +0 -2421
  67. package/dist/request-DXXGHko0.js +0 -157
  68. package/dist/session-catalog-home-id-BNwAJ9D7.js +0 -18
  69. package/dist/session-retirement-CcNC3YSY.js +0 -36
  70. package/dist/settled-turn-finalizer-Dc3ApjU4.js +0 -328
  71. package/dist/thread-resume-BE9qNEW0.js +0 -30
  72. package/dist/transport-stdio-DhoGy33Z.js +0 -99
  73. package/dist/upstream-session-fork-DlaHxSLC.js +0 -326
@@ -1,41 +1,45 @@
1
- import { h as readCodexPluginConfig, p as isCodexRemoteExecPlacementSandbox } from "./config-giW8X0QA.js";
2
- import { K as createCodexElicitationResponse, gt as CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE, p as releaseLeasedSharedCodexAppServerClient, vt as isJsonObject } from "./shared-client-DKz23hHY.js";
3
- import { E as itemStatus, F as readItem, b as auditNativeToolUnfinishedStatus, h as isCodexNotificationForTurn, v as auditNativeToolName, y as auditNativeToolTerminalStatus } from "./attempt-client-cleanup-bfsmcQ6m.js";
4
- import { l as readCodexTurn } from "./protocol-validators-VeJxxJ0U.js";
5
- import { $ as isForcedPrivateQaCodexRuntime, D as withDynamicToolExecutionState, E as createFailedDynamicToolResponse, F as resolveCodexWebSearchPlan, O as withDynamicToolTranscriptDetails, Q as filterCodexDynamicToolsForDisabledNativeSurface, T as resolveCodexToolAbortTerminalReason, Z as filterCodexDynamicTools, d as emitCodexNativePreToolUseFailureDiagnostic, nt as normalizeCodexDynamicToolName, tt as isSystemAgentOnlyCodexDynamicToolAllowlist } from "./thread-lifecycle-L7oEkTlq.js";
1
+ import { $t as isJsonObject, Qt as flattenCodexDynamicToolFunctions, Zt as CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE, _t as resolveCodexToolAbortTerminalReason, bt as withDynamicToolTranscriptDetails, it as readCodexTurn, ot as createCodexElicitationResponse, vt as createFailedDynamicToolResponse, yt as withDynamicToolExecutionState } from "./shared-client-DxcfF9KX.js";
2
+ import { E as readCodexPluginConfig, w as isCodexRemoteExecPlacementSandbox } from "./config-BhmfC1s6.js";
3
+ import { E as itemStatus, I as readItem, b as auditNativeToolUnfinishedStatus, h as isCodexNotificationForTurn, v as auditNativeToolName, y as auditNativeToolTerminalStatus } from "./attempt-client-cleanup-DYHcRQAa.js";
4
+ import { B as emitCodexNativePreToolUseFailureDiagnostic, S as resolveCodexWebSearchPlan, at as filterCodexDynamicTools, lt as isSystemAgentOnlyCodexDynamicToolAllowlist, ot as filterCodexDynamicToolsForDisabledNativeSurface, st as isForcedPrivateQaCodexRuntime, ut as normalizeCodexDynamicToolName } from "./thread-lifecycle-DAcEJ_Om.js";
5
+ import { l as resolveCodexNativeExecutionPolicy, u as resolveCodexNodeExecToolOverrides } from "./request-DYAI5FQE.js";
6
6
  import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-CnfoUPCE.js";
7
- import { r as formatCodexDisplayText } from "./command-formatters-DEsIufkt.js";
8
- import { a as resolveCodexNodeExecToolOverrides, i as resolveCodexNativeExecutionPolicy } from "./sandbox-guard-kuWTn9C5.js";
9
- import { a as sanitizeCodexApprovalVisibleText, c as waitForPluginApprovalDecision, i as requestPluginApproval, n as codexApprovalTimeoutText, o as stripDanglingCodexApprovalTerminalSequence, r as mapExecDecisionToOutcome, s as truncateCodexApprovalDisplayText, t as approvalRequestExplicitlyUnavailable } from "./plugin-approval-roundtrip-bUm777tR.js";
7
+ import { a as formatCodexDisplayText } from "./command-formatters-CLHhEB1Q.js";
8
+ import { a as requestPluginApprovalOutcome, c as truncateCodexApprovalDisplayText, i as requestPluginApproval, l as waitForPluginApprovalDecision, n as codexApprovalTimeoutText, o as sanitizeCodexApprovalVisibleText, r as mapExecDecisionToOutcome, s as stripDanglingCodexApprovalTerminalSequence, t as approvalRequestExplicitlyUnavailable } from "./plugin-approval-roundtrip-CNe-Xrjx.js";
9
+ import { a as CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME, c as createNodeExecAliasDynamicTool, i as CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME, l as isCodexDynamicToolExcluded, o as createGatewayExecProjection, r as CODEX_GATEWAY_EXEC_DYNAMIC_TOOL_NAME, s as createGatewayProcessProjection } from "./provider-capabilities-DW5p4k9w.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
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
+ import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
16
17
  import { createHash, randomUUID } from "node:crypto";
17
18
  import { resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
18
19
  import { formatErrorMessage as formatErrorMessage$1, redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
19
20
  import { killProcessTree } from "openclaw/plugin-sdk/process-runtime";
20
21
  import { SsrFBlockedError, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
21
22
  import { spawn } from "node:child_process";
23
+ import { once } from "node:events";
22
24
  import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
23
25
  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";
26
+ import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
25
27
  import { isIP } from "node:net";
26
28
  import { WebSocketServer } from "ws";
27
29
  import { fileURLToPath, pathToFileURL } from "node:url";
28
30
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
29
31
  import { redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
30
- import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
31
- import { runWithCronCreatorAuthorityCapabilityResolver } from "openclaw/plugin-sdk/codex-mcp-projection";
32
- import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
32
+ import { formatMcpCodexApprovalRemedy, requiresMcpCodexToolApproval, resolveCodexScheduledToolProjectionFactory, resolveProjectedMcpCodexToolApprovalMode, runWithCronCreatorAuthorityCapabilityResolver } from "openclaw/plugin-sdk/codex-mcp-projection";
33
33
  import { buildRemoteCommand, isToolAllowed, sanitizeEnvVars } from "openclaw/plugin-sdk/sandbox";
34
34
  import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
35
35
  import { getMediaDir } from "openclaw/plugin-sdk/media-runtime";
36
- import { normalizeOpenAIToolSchemas } from "openclaw/plugin-sdk/provider-tools";
36
+ import { consumeTrustedToolNoStartError, getCoreTtsToolResultMediaUrls } from "openclaw/plugin-sdk/agent-harness-tool-runtime";
37
+ import { normalizeOpenAIStrictCompatSchema } from "openclaw/plugin-sdk/provider-tools";
37
38
  //#region extensions/codex/src/app-server/event-projector-native-tool-lifecycle.ts
38
- /** Projects metadata-only lifecycle diagnostics for native tool items. */
39
+ function isMcpToolCallItemNotification(method, params) {
40
+ return (method === "item/started" || method === "item/completed") && isJsonObject(params.item) && params.item.type === "mcpToolCall";
41
+ }
42
+ /** Owns native item lifetimes for diagnostics and MCP approval correlation. */
39
43
  var CodexNativeToolLifecycleProjector = class {
40
44
  constructor(context, threadId, turnId, options = {}) {
41
45
  this.context = context;
@@ -49,13 +53,39 @@ var CodexNativeToolLifecycleProjector = class {
49
53
  this.approvalFailureDispositionByItem = /* @__PURE__ */ new Map();
50
54
  this.preToolUseFailureByItem = /* @__PURE__ */ new Map();
51
55
  this.finalized = false;
56
+ this.turnCompleted = false;
57
+ this.pendingMcpNotifications = 0;
58
+ }
59
+ getActiveMcpToolCall(serverName) {
60
+ if (this.finalized || this.turnCompleted || this.pendingMcpNotifications > 0 || this.options.runAbortSignal?.aborted) return;
61
+ let candidate;
62
+ for (const { mcpToolCall } of this.activeItems.values()) {
63
+ if (mcpToolCall?.server !== serverName) continue;
64
+ if (candidate) return;
65
+ candidate = mcpToolCall;
66
+ }
67
+ if (!candidate || candidate.status !== "inProgress" || typeof candidate.server !== "string" || !candidate.server.trim() || typeof candidate.tool !== "string" || !candidate.tool.trim() || candidate.arguments === void 0 || candidate.appContext != null || candidate.pluginId != null) return;
68
+ return {
69
+ id: candidate.id,
70
+ server: candidate.server,
71
+ tool: candidate.tool,
72
+ arguments: candidate.arguments
73
+ };
74
+ }
75
+ recordMcpToolCallReceipt(notification) {
76
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
77
+ if (!params || !isCodexNotificationForTurn(params, this.threadId, this.turnId)) return;
78
+ if (isMcpToolCallItemNotification(notification.method, params)) this.pendingMcpNotifications += 1;
79
+ else if (notification.method === "turn/completed" && readCodexTurn(params.turn)?.id === this.turnId) this.turnCompleted = true;
52
80
  }
53
81
  handleNotification(notification) {
54
82
  const params = isJsonObject(notification.params) ? notification.params : void 0;
55
83
  if (!params || !isCodexNotificationForTurn(params, this.threadId, this.turnId)) return;
84
+ if (this.pendingMcpNotifications > 0 && isMcpToolCallItemNotification(notification.method, params)) this.pendingMcpNotifications -= 1;
56
85
  if (notification.method === "turn/completed") {
57
86
  const turn = readCodexTurn(params.turn);
58
87
  if (!turn || turn.id !== this.turnId) return;
88
+ this.turnCompleted = true;
59
89
  for (const item of turn.items ?? []) this.recordSnapshotItem(item);
60
90
  return;
61
91
  }
@@ -74,10 +104,10 @@ var CodexNativeToolLifecycleProjector = class {
74
104
  });
75
105
  }
76
106
  recordItem(params) {
77
- const toolName = auditNativeToolName(params.item);
107
+ const toolName = auditNativeToolName(params.item) ?? (params.item.type === "mcpToolCall" ? params.item.type : void 0);
78
108
  if (!toolName || this.completedItemIds.has(params.item.id)) return;
79
109
  if (params.phase === "start") {
80
- this.recordStarted(params.item.id, toolName, auditNativeToolUnfinishedStatus(params.item), params.sourceTimestampMs);
110
+ this.recordStarted(params.item.id, toolName, auditNativeToolUnfinishedStatus(params.item), params.sourceTimestampMs, params.item.type === "mcpToolCall" ? params.item : void 0);
81
111
  return;
82
112
  }
83
113
  if (params.item.type === "webSearch") {
@@ -203,12 +233,13 @@ var CodexNativeToolLifecycleProjector = class {
203
233
  item
204
234
  });
205
235
  }
206
- recordStarted(toolCallId, toolName, unfinishedStatus, sourceTimestampMs) {
236
+ recordStarted(toolCallId, toolName, unfinishedStatus, sourceTimestampMs, mcpToolCall) {
207
237
  if (this.activeItems.has(toolCallId)) return;
208
238
  this.startedAtByItem.set(toolCallId, sourceTimestampMs ?? Date.now());
209
239
  this.activeItems.set(toolCallId, {
210
240
  toolName,
211
- unfinishedStatus
241
+ unfinishedStatus,
242
+ mcpToolCall
212
243
  });
213
244
  emitTrustedDiagnosticEvent({
214
245
  type: "tool.execution.started",
@@ -278,10 +309,10 @@ function joinRemoteWorkspacePath(remoteRoot, suffix) {
278
309
  //#endregion
279
310
  //#region extensions/codex/src/app-server/remote-workspace-media.ts
280
311
  const REMOTE_WORKSPACE_MEDIA_TIMEOUT_MS = 6e4;
281
- const REMOTE_WORKSPACE_MEDIA_MAX_BYTES = 64 * 1024 * 1024;
312
+ const REMOTE_WORKSPACE_MEDIA_MAX_BYTES = 67108864;
282
313
  const REMOTE_WORKSPACE_MEDIA_MAX_ATTACHMENTS = 16;
283
- const CODEX_REMOTE_MEDIA_CHUNK_BYTES = 512 * 1024;
284
- const CODEX_REMOTE_COMMAND_DEFAULT_OUTPUT_BYTES = 1024 * 1024;
314
+ const CODEX_REMOTE_MEDIA_CHUNK_BYTES = 524288;
315
+ const CODEX_REMOTE_COMMAND_DEFAULT_OUTPUT_BYTES = 1048576;
285
316
  const CODEX_BOUNDED_REMOTE_FILE_READER = [
286
317
  "try{",
287
318
  "const fs=require(\"node:fs\");",
@@ -543,90 +574,6 @@ function mapMessageMediaValues(args, mapValue) {
543
574
  /** Mutable dependency seam shared by dynamic-tool construction and its behavioral tests. */
544
575
  const dynamicToolBuildState = {};
545
576
  //#endregion
546
- //#region extensions/codex/src/app-server/shell-dynamic-tools.ts
547
- const CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME = "node_exec";
548
- const CODEX_GATEWAY_EXEC_DYNAMIC_TOOL_NAME = "gateway_exec";
549
- const CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME = "gateway_process";
550
- const CODEX_EXEC_POLICY_PARAMETER_NAMES = /* @__PURE__ */ new Set([
551
- "host",
552
- "security",
553
- "ask"
554
- ]);
555
- const CODEX_NODE_EXEC_PARAMETER_NAMES = /* @__PURE__ */ new Set([
556
- "command",
557
- "workdir",
558
- "env",
559
- "timeoutSeconds",
560
- "node"
561
- ]);
562
- const PROCESS_FOLLOWUP_TEXT = "Use process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.";
563
- /** Returns true when plugin config explicitly removes any named dynamic tool. */
564
- function isCodexDynamicToolExcluded(config, names) {
565
- const normalizedNames = new Set(names.map((name) => normalizeCodexDynamicToolName(name)));
566
- return (config.codexDynamicToolsExclude ?? []).some((name) => normalizedNames.has(normalizeCodexDynamicToolName(name)));
567
- }
568
- function createExecAliasDynamicTool(execTool, params) {
569
- const pinnedNode = params.host === "node" ? params.node?.trim() : void 0;
570
- const nodeAlias = params.host === "node";
571
- const gatewayProcessAliasAvailable = params.host === "gateway" && params.processAliasAvailable;
572
- const name = nodeAlias ? CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME : CODEX_GATEWAY_EXEC_DYNAMIC_TOOL_NAME;
573
- 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.";
574
- 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.";
575
- return {
576
- ...execTool,
577
- name,
578
- description,
579
- parameters: hideExecDynamicToolParameters(execTool.parameters, !nodeAlias || Boolean(pinnedNode), nodeAlias),
580
- execute: async (toolCallId, args, signal, onUpdate) => {
581
- const result = await execTool.execute(toolCallId, pinExecDynamicToolArgs(args, params, pinnedNode), signal, onUpdate);
582
- return {
583
- ...result,
584
- content: result.content.map((item) => item.type === "text" ? Object.assign({}, item, { text: item.text.replace(PROCESS_FOLLOWUP_TEXT, followupText) }) : item)
585
- };
586
- }
587
- };
588
- }
589
- function createGatewayProcessAliasDynamicTool(processTool) {
590
- return {
591
- ...processTool,
592
- name: CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME,
593
- description: "Manage background shell sessions in the existing per-session OpenClaw process scope: list, poll, log, write, send-keys, submit, paste, kill, clear, or remove. Use for gateway_exec follow-up; use native Codex shell session handling for ordinary local work."
594
- };
595
- }
596
- function pinExecDynamicToolArgs(args, params, configuredNode) {
597
- const { host: _host, security: _security, ask: _ask, node: requestedNode, ...rest } = normalizeExecDynamicToolArgs(args);
598
- if (params.host === "gateway") return {
599
- ...rest,
600
- host: params.host,
601
- ...params.ask ? { ask: params.ask } : {}
602
- };
603
- const nodeArgs = Object.fromEntries(Object.entries(rest).filter(([name]) => CODEX_NODE_EXEC_PARAMETER_NAMES.has(name)));
604
- const node = configuredNode ?? (typeof requestedNode === "string" ? requestedNode.trim() : "");
605
- return {
606
- ...nodeArgs,
607
- host: params.host,
608
- ...node ? { node } : {}
609
- };
610
- }
611
- function normalizeExecDynamicToolArgs(args) {
612
- return args && typeof args === "object" && !Array.isArray(args) ? args : {};
613
- }
614
- function hideExecDynamicToolParameters(parameters, hideNode, nodeOnly) {
615
- if (!parameters || typeof parameters !== "object" || Array.isArray(parameters)) return parameters;
616
- const schema = parameters;
617
- const rawProperties = schema.properties;
618
- if (!rawProperties || typeof rawProperties !== "object" || Array.isArray(rawProperties)) return parameters;
619
- 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");
620
- const nextProperties = Object.fromEntries(Object.entries(rawProperties).filter(([name]) => includeParameter(name)));
621
- const rawRequired = schema.required;
622
- const nextRequired = Array.isArray(rawRequired) ? rawRequired.filter((name) => typeof name !== "string" || includeParameter(name)) : rawRequired;
623
- return {
624
- ...schema,
625
- properties: nextProperties,
626
- ...Array.isArray(rawRequired) ? { required: nextRequired } : {}
627
- };
628
- }
629
- //#endregion
630
577
  //#region extensions/codex/src/app-server/vision-tools.ts
631
578
  /**
632
579
  * Codex's enabled native surface includes its stable view_image loader. Keep
@@ -652,6 +599,15 @@ const CODEX_NATIVE_SANDBOX_TOOL_REQUIREMENTS = [
652
599
  "apply_patch"
653
600
  ];
654
601
  const CODEX_MEMORY_FLUSH_DYNAMIC_TOOL_ALLOW = /* @__PURE__ */ new Set(["read", "write"]);
602
+ const CODEX_DISABLED_NATIVE_SHELL_DYNAMIC_TOOLS = /* @__PURE__ */ new Set([
603
+ "exec",
604
+ "process",
605
+ "sandbox_exec",
606
+ "sandbox_process",
607
+ CODEX_GATEWAY_EXEC_DYNAMIC_TOOL_NAME,
608
+ CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME,
609
+ CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME
610
+ ]);
655
611
  /** Keeps node filesystem and process ownership on its native exec-server. */
656
612
  function resolveCodexNodePlacementToolConstructionPlan(sandbox, nativeToolSurfaceEnabled) {
657
613
  if (!isCodexRemoteExecPlacementSandbox(sandbox) || sandbox?.backendId !== "node" || !("placementNodeId" in sandbox) || typeof sandbox.placementNodeId !== "string" || !sandbox.placementNodeId) return;
@@ -669,13 +625,6 @@ function preserveRingZeroSystemAgentTool(allTools, filteredTools) {
669
625
  if (!openclaw) return filteredTools;
670
626
  return [openclaw, ...filteredTools.filter((tool) => tool.name !== "openclaw")];
671
627
  }
672
- /** Splits sandbox and run session keys so tool calls can bind to both scopes when needed. */
673
- function resolveOpenClawCodingToolsSessionKeys(params, sandboxSessionKey) {
674
- return {
675
- sessionKey: sandboxSessionKey,
676
- runSessionKey: params.sessionKey && params.sessionKey !== sandboxSessionKey ? params.sessionKey : void 0
677
- };
678
- }
679
628
  /** Returns the canonical channel used for Codex message routing and receipts. */
680
629
  function resolveCodexMessageToolProvider(params) {
681
630
  return params.messageChannel ?? params.messageProvider;
@@ -740,6 +689,10 @@ async function buildDynamicTools(input) {
740
689
  ...params,
741
690
  disableMessageTool: false
742
691
  } : params;
692
+ const toolRunContext = buildEmbeddedAttemptToolRunContext({
693
+ ...params,
694
+ forceMessageTool: shouldForceMessageTool(messagePolicyParams)
695
+ });
743
696
  if (params.disableTools) {
744
697
  input.onWebSearchPolicyResolved?.(false);
745
698
  return [];
@@ -753,11 +706,11 @@ async function buildDynamicTools(input) {
753
706
  const modelHasVision = params.model.input?.includes("image") ?? false;
754
707
  const agentDir = params.agentDir ?? resolveAgentDir$1(params.config ?? {}, input.sessionAgentId);
755
708
  const injectedOpenClawCodingToolsFactory = dynamicToolBuildState.openClawCodingToolsFactory;
756
- let agentHarnessModule;
757
- const loadAgentHarnessModule = async () => agentHarnessModule ??= await import("openclaw/plugin-sdk/agent-harness");
758
- toolBuildStages.mark("load-agent-harness-tools");
759
- const sessionKeys = resolveOpenClawCodingToolsSessionKeys(params, input.sandboxSessionKey);
760
- const nativeExecutionPolicy = resolveCodexNativeExecutionPolicyForDynamicTools(input);
709
+ const nativeExecutionPolicy = resolveCodexNativeExecutionPolicyForRun(params, {
710
+ agentId: input.policyAgentId,
711
+ runtimeSessionKey: input.sandboxSessionKey,
712
+ sandbox: input.sandbox
713
+ });
761
714
  const webSearchPlan = resolveCodexWebSearchPlan({
762
715
  config: params.config,
763
716
  disableTools: params.disableTools,
@@ -765,109 +718,114 @@ async function buildDynamicTools(input) {
765
718
  nativeProviderWebSearchSupport: input.nativeProviderWebSearchSupport
766
719
  });
767
720
  const webFetchHostnameAllowlistRef = {};
768
- const buildOpenClawCodingTools = () => {
769
- const toolConstructionPlan = resolveCodexNodePlacementToolConstructionPlan(input.sandbox, input.nativeToolSurfaceEnabled);
770
- const options = {
771
- agentId: input.sessionAgentId,
772
- ...buildEmbeddedAttemptToolRunContext(params),
773
- exec: {
774
- ...params.execOverrides,
775
- ...input.sessionPermissionPolicy ? { mode: input.sessionPermissionPolicy.execMode } : {},
776
- ...resolveCodexNodeExecToolOverrides(nativeExecutionPolicy),
777
- config: params.config,
778
- elevated: params.bashElevated
779
- },
780
- sessionPermissionPolicy: input.sessionPermissionPolicy ? {
781
- mode: input.sessionPermissionPolicy.mode,
782
- root: input.sessionPermissionPolicy.root
783
- } : void 0,
784
- sandbox: input.sandbox,
785
- ...toolConstructionPlan ? { toolConstructionPlan } : {},
786
- messageProvider: resolveCodexMessageToolProvider(params),
787
- toolPolicyMessageProvider: params.messageProvider ?? params.messageChannel,
788
- clientCaps: params.clientCaps,
789
- chatType: params.chatType,
790
- agentAccountId: params.agentAccountId,
791
- messageTo: params.messageTo,
792
- messageThreadId: params.messageThreadId,
793
- nativeChannelId: params.chatId,
794
- messageActionTurnCapability: params.messageActionTurnCapability,
795
- groupId: params.groupId,
796
- groupChannel: params.groupChannel,
797
- groupSpace: params.groupSpace,
798
- spawnedBy: params.spawnedBy,
799
- senderId: params.senderId,
800
- senderName: params.senderName,
801
- senderUsername: params.senderUsername,
802
- senderE164: params.senderE164,
803
- senderIsOwner: params.senderIsOwner,
804
- inputProvenance: params.inputProvenance,
805
- trustedInternalHandoff: params.trustedInternalHandoff,
806
- scheduledToolPolicy: params.scheduledToolPolicy,
807
- allowGatewaySubagentBinding: params.allowGatewaySubagentBinding || isForcedPrivateQaCodexRuntime(),
808
- ...sessionKeys,
809
- sessionId: params.sessionId,
810
- runId: params.runId,
811
- approvalReviewerDeviceId: params.approvalReviewerDeviceId,
812
- agentDir,
813
- preparedModelRuntime: params.preparedModelRuntime,
814
- cwd: input.effectiveCwd ?? input.effectiveWorkspace,
815
- workspaceDir: input.effectiveWorkspace,
816
- spawnWorkspaceDir: input.effectiveCwd && input.effectiveCwd !== input.effectiveWorkspace ? input.resolvedWorkspace : resolveAttemptSpawnWorkspaceDir({
817
- sandbox: input.sandbox,
818
- resolvedWorkspace: input.resolvedWorkspace
819
- }),
721
+ const toolConstructionPlan = resolveCodexNodePlacementToolConstructionPlan(input.sandbox, input.nativeToolSurfaceEnabled);
722
+ const options = {
723
+ agentId: input.sessionAgentId,
724
+ policyAgentId: input.policyAgentId,
725
+ ...toolRunContext,
726
+ exec: {
727
+ ...params.execOverrides,
728
+ ...input.sessionPermissionPolicy ? { mode: input.sessionPermissionPolicy.execMode } : {},
729
+ ...resolveCodexNodeExecToolOverrides(nativeExecutionPolicy),
820
730
  config: params.config,
821
- githubPublicationAvailable: params.githubPublicationAvailable,
822
- authProfileStore: params.toolAuthProfileStore ?? params.authProfileStore,
823
- abortSignal: input.runAbortController.signal,
824
- emitBeforeToolCallDiagnostics: false,
825
- modelProvider: params.model.provider,
826
- modelId: params.modelId,
827
- modelCompat: params.model.compat && typeof params.model.compat === "object" ? params.model.compat : void 0,
828
- modelApi: params.model.api,
829
- modelContextWindowTokens: params.model.contextWindow,
830
- delegationCapability: params.delegationCapability,
831
- modelAuthMode: resolveModelAuthMode(params.model.provider, params.config, params.toolAuthProfileStore ?? params.authProfileStore, { workspaceDir: input.effectiveWorkspace }),
832
- suppressManagedWebSearch: false,
833
- webFetchHostnameAllowlistRef,
834
- currentChannelId: params.currentChannelId,
835
- currentMessagingTarget: params.currentMessagingTarget,
836
- hookChannelId: resolveCodexAppServerHookChannelId(params, input.sandboxSessionKey),
837
- currentThreadTs: params.currentThreadTs,
838
- currentMessageId: params.currentMessageId,
839
- replyToMode: params.replyToMode,
840
- hasRepliedRef: params.hasRepliedRef,
841
- modelHasVision,
842
- computerContextEpoch: input.computerContextEpoch,
843
- requireExplicitMessageTarget: params.requireExplicitMessageTarget ?? isSubagentSessionKey(params.sessionKey),
844
- sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
845
- taskSuggestionDeliveryMode: params.taskSuggestionDeliveryMode,
846
- disableMessageTool: input.ignoreDisableMessageTool ? false : params.disableMessageTool,
847
- forceMessageTool: shouldForceMessageTool(messagePolicyParams),
848
- enableHeartbeatTool: params.trigger === "heartbeat" || input.forceHeartbeatTool === true,
849
- forceHeartbeatTool: params.trigger === "heartbeat" || input.forceHeartbeatTool === true,
850
- onYield: (message, acknowledgment) => {
851
- input.onYieldDetected(acknowledgment);
852
- input.onCodexAppServerEvent?.({
853
- stream: "codex_app_server.tool",
854
- data: {
855
- name: "sessions_yield",
856
- message
857
- }
858
- });
859
- },
860
- claimYieldCompletion: input.claimYieldCompletion,
861
- recordToolPrepStage: (name) => {
862
- toolBuildStages.mark(name);
863
- },
864
- onToolOutcome: params.onToolOutcome,
865
- isTurnTainted: params.isTurnTainted,
866
- allocateToolOutcomeOrdinal: params.allocateToolOutcomeOrdinal,
867
- cronCreatorToolAllowlistRef: input.cronCreatorToolAllowlistRef,
868
- cronCreatorToolAllowlistCaptureRef: input.cronCreatorToolAllowlistCaptureRef,
869
- cronCreatorAuthorityUnavailableReason: input.cronCreatorAuthorityUnavailableReason
870
- };
731
+ elevated: params.bashElevated
732
+ },
733
+ sessionPermissionPolicy: input.sessionPermissionPolicy ? {
734
+ mode: input.sessionPermissionPolicy.mode,
735
+ root: input.sessionPermissionPolicy.root
736
+ } : void 0,
737
+ sandbox: input.sandbox,
738
+ ...toolConstructionPlan ? { toolConstructionPlan } : {},
739
+ messageProvider: resolveCodexMessageToolProvider(params),
740
+ toolPolicyMessageProvider: params.messageProvider ?? params.messageChannel,
741
+ clientCaps: params.clientCaps,
742
+ chatType: params.chatType,
743
+ agentAccountId: params.agentAccountId,
744
+ messageTo: params.messageTo,
745
+ messageThreadId: params.messageThreadId,
746
+ nativeChannelId: params.chatId,
747
+ messageActionTurnCapability: params.messageActionTurnCapability,
748
+ groupId: params.groupId,
749
+ groupChannel: params.groupChannel,
750
+ groupSpace: params.groupSpace,
751
+ spawnedBy: params.spawnedBy,
752
+ senderId: params.senderId,
753
+ senderName: params.senderName,
754
+ senderUsername: params.senderUsername,
755
+ senderE164: params.senderE164,
756
+ senderIsOwner: params.senderIsOwner,
757
+ inputProvenance: params.inputProvenance,
758
+ trustedInternalHandoff: params.trustedInternalHandoff,
759
+ scheduledToolPolicy: params.scheduledToolPolicy,
760
+ allowGatewaySubagentBinding: params.allowGatewaySubagentBinding || isForcedPrivateQaCodexRuntime(),
761
+ sessionKey: input.sandboxSessionKey,
762
+ runSessionKey: params.sessionKey && params.sessionKey !== input.sandboxSessionKey ? params.sessionKey : void 0,
763
+ sessionId: params.sessionId,
764
+ runId: params.runId,
765
+ approvalReviewerDeviceId: params.approvalReviewerDeviceId,
766
+ agentDir,
767
+ preparedModelRuntime: params.preparedModelRuntime,
768
+ cwd: input.effectiveCwd ?? input.effectiveWorkspace,
769
+ workspaceDir: input.effectiveWorkspace,
770
+ spawnWorkspaceDir: input.effectiveCwd && input.effectiveCwd !== input.effectiveWorkspace ? input.resolvedWorkspace : resolveAttemptSpawnWorkspaceDir({
771
+ sandbox: input.sandbox,
772
+ resolvedWorkspace: input.resolvedWorkspace
773
+ }),
774
+ config: params.config,
775
+ skillsSnapshot: params.skillsSnapshot,
776
+ ...params.skillLibraryAuthoring ? { skillWorkshop: { libraryAuthoring: params.skillLibraryAuthoring } } : {},
777
+ githubPublicationAvailable: params.githubPublicationAvailable,
778
+ authProfileStore: params.toolAuthProfileStore ?? params.authProfileStore,
779
+ abortSignal: input.runAbortController.signal,
780
+ emitBeforeToolCallDiagnostics: false,
781
+ modelProvider: params.model.provider,
782
+ modelId: params.modelId,
783
+ modelCompat: params.model.compat && typeof params.model.compat === "object" ? params.model.compat : void 0,
784
+ modelApi: params.model.api,
785
+ modelContextWindowTokens: params.model.contextWindow,
786
+ delegationCapability: params.delegationCapability,
787
+ modelAuthMode: resolveModelAuthMode(params.model.provider, params.config, params.toolAuthProfileStore ?? params.authProfileStore, { workspaceDir: input.effectiveWorkspace }),
788
+ suppressManagedWebSearch: false,
789
+ webFetchHostnameAllowlistRef,
790
+ currentChannelId: params.currentChannelId,
791
+ currentMessagingTarget: params.currentMessagingTarget,
792
+ hookChannelId: resolveCodexAppServerHookChannelId(params, input.sandboxSessionKey),
793
+ currentThreadTs: params.currentThreadTs,
794
+ currentMessageId: params.currentMessageId,
795
+ replyToMode: params.replyToMode,
796
+ hasRepliedRef: params.hasRepliedRef,
797
+ modelHasVision,
798
+ computerContextEpoch: input.computerContextEpoch,
799
+ registerRunCleanup: input.registerRunCleanup,
800
+ requireExplicitMessageTarget: params.requireExplicitMessageTarget ?? isSubagentSessionKey(params.sessionKey),
801
+ sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
802
+ taskSuggestionDeliveryMode: params.taskSuggestionDeliveryMode,
803
+ disableMessageTool: input.ignoreDisableMessageTool ? false : params.disableMessageTool,
804
+ forceMessageTool: shouldForceMessageTool(messagePolicyParams),
805
+ enableHeartbeatTool: params.trigger === "heartbeat" || input.forceHeartbeatTool === true,
806
+ forceHeartbeatTool: params.trigger === "heartbeat" || input.forceHeartbeatTool === true,
807
+ onYield: (message, acknowledgment) => {
808
+ input.onYieldDetected(acknowledgment);
809
+ input.onCodexAppServerEvent?.({
810
+ stream: "codex_app_server.tool",
811
+ data: {
812
+ name: "sessions_yield",
813
+ message
814
+ }
815
+ });
816
+ },
817
+ claimYieldCompletion: input.claimYieldCompletion,
818
+ recordToolPrepStage: (name) => {
819
+ toolBuildStages.mark(name);
820
+ },
821
+ onToolOutcome: params.onToolOutcome,
822
+ isTurnTainted: params.isTurnTainted,
823
+ allocateToolOutcomeOrdinal: params.allocateToolOutcomeOrdinal,
824
+ cronCreatorToolAllowlistRef: input.cronCreatorToolAllowlistRef,
825
+ cronCreatorToolAllowlistCaptureRef: input.cronCreatorToolAllowlistCaptureRef,
826
+ cronCreatorAuthorityUnavailableReason: input.cronCreatorAuthorityUnavailableReason
827
+ };
828
+ const buildOpenClawCodingTools = () => {
871
829
  const bindingOptions = { cwd: input.effectiveCwd ?? input.effectiveWorkspace };
872
830
  if (injectedOpenClawCodingToolsFactory) return params.hostCapabilities.bindToolSurface(injectedOpenClawCodingToolsFactory(options), bindingOptions);
873
831
  const createToolSurface = params.hostCapabilities.createToolSurface;
@@ -887,7 +845,7 @@ async function buildDynamicTools(input) {
887
845
  const readableAllTools = [...readableAllToolProjection.tools];
888
846
  const normallyProfiledTools = input.nativeToolSurfaceEnabled === false ? filterCodexDynamicToolsForDisabledNativeSurface(readableAllTools, input.pluginConfig, { preserveShell: shouldKeepOpenClawShellDynamicTools(input, nativeExecutionPolicy) }) : filterCodexDynamicTools(readableAllTools, input.pluginConfig);
889
847
  const profileFilteredTools = (input.isHostScopedToolActive?.("openclaw") ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow) ? preserveRingZeroSystemAgentTool(readableAllTools, normallyProfiledTools) : normallyProfiledTools;
890
- const codexFilteredTools = addNodeShellDynamicToolsIfNeeded(addGatewayShellDynamicToolsIfAvailable(addSandboxShellDynamicToolsIfAvailable(isCodexMemoryFlushRun(params) ? filterCodexMemoryFlushDynamicTools(readableAllTools) : profileFilteredTools, readableAllTools, input), readableAllTools, input, nativeExecutionPolicy), readableAllTools, input, nativeExecutionPolicy);
848
+ const codexFilteredTools = await addNodeShellDynamicToolsIfNeeded(addGatewayShellDynamicToolsIfAvailable(addSandboxShellDynamicToolsIfAvailable(isCodexMemoryFlushRun(params) ? filterCodexMemoryFlushDynamicTools(readableAllTools) : profileFilteredTools, readableAllTools, input, nativeExecutionPolicy), readableAllTools, input, nativeExecutionPolicy), readableAllTools, input, nativeExecutionPolicy);
891
849
  toolBuildStages.mark("codex-filtering");
892
850
  const visionFilteredTools = filterCodexVisionTools(codexFilteredTools, {
893
851
  modelHasVision,
@@ -896,33 +854,33 @@ async function buildDynamicTools(input) {
896
854
  toolBuildStages.mark("vision-filtering");
897
855
  const webSearchPresent = visionFilteredTools.some((tool) => tool.name === "web_search");
898
856
  const persistentCodexWebSearchSurface = params.config?.tools?.web?.search?.enabled !== false && !(input.pluginConfig.codexDynamicToolsExclude ?? []).some((name) => normalizeCodexDynamicToolName(name) === "web_search");
899
- const webSearchPolicy = webSearchPresent || persistentCodexWebSearchSurface ? (await loadAgentHarnessModule()).resolveWebSearchToolPolicy({
900
- config: params.config,
901
- modelProvider: params.model.provider,
902
- modelId: params.modelId,
903
- agentId: input.sessionAgentId,
904
- sessionKey: input.sandboxSessionKey,
905
- sandboxToolPolicy: input.sandbox?.tools,
906
- messageProvider: resolveCodexMessageToolProvider(params),
907
- agentAccountId: params.agentAccountId,
908
- groupId: params.groupId,
909
- groupChannel: params.groupChannel,
910
- groupSpace: params.groupSpace,
911
- spawnedBy: params.spawnedBy,
912
- senderId: params.senderId,
913
- senderName: params.senderName,
914
- senderUsername: params.senderUsername,
915
- senderE164: params.senderE164,
916
- inputProvenance: params.inputProvenance,
917
- trustedInternalHandoff: params.trustedInternalHandoff,
918
- scheduledToolPolicy: params.scheduledToolPolicy
919
- }) : {
920
- allowed: false,
921
- persistentAllowed: false
922
- };
923
- const transientWebSearchRestriction = !webSearchPolicy.allowed && webSearchPolicy.persistentAllowed || isCodexMemoryFlushRun(params);
924
- input.onPersistentWebSearchPolicyResolved?.(webSearchPresent || persistentCodexWebSearchSurface && transientWebSearchRestriction && webSearchPolicy.persistentAllowed);
925
- const filteredTools = filterCodexDynamicToolsForAllowlist(visionFilteredTools, includeForcedCodexDynamicToolAllow(params.toolsAllow, messagePolicyParams));
857
+ let persistentWebSearchAllowed = webSearchPresent;
858
+ if (input.onPersistentWebSearchPolicyResolved && !webSearchPresent && persistentCodexWebSearchSurface) {
859
+ const webSearchPolicy = (await import("openclaw/plugin-sdk/agent-harness")).resolveWebSearchToolPolicy({
860
+ config: params.config,
861
+ modelProvider: params.model.provider,
862
+ modelId: params.modelId,
863
+ agentId: input.policyAgentId,
864
+ sessionKey: input.sandboxSessionKey,
865
+ sandboxToolPolicy: input.sandbox?.tools,
866
+ messageProvider: resolveCodexMessageToolProvider(params),
867
+ agentAccountId: params.agentAccountId,
868
+ groupId: params.groupId,
869
+ groupChannel: params.groupChannel,
870
+ groupSpace: params.groupSpace,
871
+ spawnedBy: params.spawnedBy,
872
+ senderId: params.senderId,
873
+ senderName: params.senderName,
874
+ senderUsername: params.senderUsername,
875
+ senderE164: params.senderE164,
876
+ inputProvenance: params.inputProvenance,
877
+ trustedInternalHandoff: params.trustedInternalHandoff,
878
+ scheduledToolPolicy: params.scheduledToolPolicy
879
+ });
880
+ persistentWebSearchAllowed = webSearchPolicy.persistentAllowed && (!webSearchPolicy.allowed || isCodexMemoryFlushRun(params));
881
+ }
882
+ input.onPersistentWebSearchPolicyResolved?.(persistentWebSearchAllowed);
883
+ const filteredTools = filterCodexDynamicToolsForAllowlist(visionFilteredTools, toolRunContext.runtimeToolAllowlist);
926
884
  toolBuildStages.mark("allowlist-filter");
927
885
  const normalizedTools = normalizeAgentRuntimeTools({
928
886
  runtimePlan: input.ignoreRuntimePlan ? void 0 : params.runtimePlan,
@@ -941,7 +899,7 @@ async function buildDynamicTools(input) {
941
899
  const webSearchAllowed = normalizedTools.some((tool) => tool.name === "web_search");
942
900
  webFetchHostnameAllowlistRef.value = webSearchAllowed ? webSearchPlan.webFetchHostnameAllowlist : void 0;
943
901
  input.onWebSearchPolicyResolved?.(webSearchAllowed);
944
- const exposedTools = webSearchPlan.suppressManagedWebSearch ? normalizedTools.filter((tool) => tool.name !== "web_search") : normalizedTools;
902
+ const exposedTools = placeDisabledNativeShellToolsInDirectNamespace(webSearchPlan.suppressManagedWebSearch ? normalizedTools.filter((tool) => tool.name !== "web_search") : normalizedTools, input.nativeToolSurfaceEnabled);
945
903
  if (preNormalizationDiagnostics.length > 0) embeddedAgentLog.warn(`codex app-server quarantined ${preNormalizationDiagnostics.length} unsupported runtime tool schema${preNormalizationDiagnostics.length === 1 ? "" : "s"} before dynamic tool registration`, {
946
904
  runId: params.runId,
947
905
  sessionId: params.sessionId,
@@ -983,49 +941,39 @@ function addGatewayShellDynamicToolsIfAvailable(filteredTools, allTools, input,
983
941
  if (!execTool || execExcluded || existingNames.has("gateway_exec")) return filteredTools;
984
942
  const processExcluded = isCodexDynamicToolExcluded(input.pluginConfig, ["process", CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME]);
985
943
  const processAliasAvailable = Boolean(processTool && !processExcluded && !existingNames.has("gateway_process"));
986
- const toolsToAppend = [createExecAliasDynamicTool(execTool, {
987
- host: "gateway",
944
+ const createProjection = resolveCodexScheduledToolProjectionFactory(input.params.hostCapabilities);
945
+ if (!createProjection) return filteredTools;
946
+ const toolsToAppend = [createGatewayExecProjection(createProjection, execTool, {
988
947
  processAliasAvailable,
989
948
  ...input.sessionPermissionPolicy?.mode === "guarded" ? { ask: "always" } : {}
990
949
  })];
991
- if (processAliasAvailable && processTool) toolsToAppend.push(createGatewayProcessAliasDynamicTool(processTool));
950
+ if (processAliasAvailable && processTool) toolsToAppend.push(createGatewayProcessProjection(createProjection, processTool));
992
951
  return [...filteredTools, ...toolsToAppend];
993
952
  }
994
- /** Preserves delivery-critical tools when a narrow allowlist would otherwise hide them. */
995
- function includeForcedCodexDynamicToolAllow(toolsAllow, params) {
996
- if (toolsAllow === void 0 || hasWildcardCodexToolsAllow(toolsAllow)) return toolsAllow;
997
- const forcedToolNames = shouldForceMessageTool(params) ? ["message"] : [];
998
- if (forcedToolNames.length === 0) return toolsAllow;
999
- if (toolsAllow.length === 0) return forcedToolNames;
1000
- const normalized = new Set(toolsAllow.map((name) => normalizeCodexDynamicToolName(name)));
1001
- const missingToolNames = forcedToolNames.filter((toolName) => !normalized.has(normalizeCodexDynamicToolName(toolName)));
1002
- return missingToolNames.length === 0 ? toolsAllow : [...toolsAllow, ...missingToolNames];
1003
- }
1004
953
  /** Decides whether Codex native code mode can own shell/file tools for this turn. */
1005
954
  function shouldEnableCodexAppServerNativeToolSurface(params, sandbox, options = {}) {
1006
955
  if (params.pluginHarnessToolPolicyRestricted === true) return false;
1007
956
  if (isCodexMemoryFlushRun(params)) return false;
1008
957
  if (params.disableTools) return false;
1009
- if (isCodexNativeExecutionBlockedByNodeExecHost(params, {
958
+ if (!resolveCodexNativeExecutionPolicyForRun(params, {
1010
959
  agentId: options.agentId,
1011
960
  runtimeSessionKey: options.runtimeSessionKey,
1012
961
  sandbox
1013
- })) return false;
1014
- const toolsAllow = includeForcedCodexDynamicToolAllow(params.toolsAllow, params);
962
+ }).nativeToolSurfaceAllowed) return false;
963
+ const toolsAllow = params.toolsAllow;
1015
964
  if (toolsAllow === void 0) return canCodexAppServerNativeToolSurfaceHonorSandbox(sandbox, options);
1016
965
  return hasWildcardCodexToolsAllow(toolsAllow) && canCodexAppServerNativeToolSurfaceHonorSandbox(sandbox, options);
1017
966
  }
1018
- /** Returns true when OpenClaw policy requires the Node-owned exec/process tools instead. */
1019
- function isCodexNativeExecutionBlockedByNodeExecHost(params, options = {}) {
1020
- return !resolveCodexNativeExecutionPolicy({
967
+ function resolveCodexNativeExecutionPolicyForRun(params, options = {}) {
968
+ return resolveCodexNativeExecutionPolicy({
1021
969
  config: params.config,
1022
970
  sessionKey: resolveCodexRuntimePolicySessionKey(params, options.runtimeSessionKey),
1023
971
  sessionId: params.sessionId,
1024
972
  agentId: options.agentId,
1025
973
  execOverrides: params.execOverrides,
1026
- sandboxAvailable: options.sandbox?.enabled,
974
+ sandboxAvailable: options.sandbox === null ? false : options.sandbox?.enabled,
1027
975
  readRuntimeSessionEntry: true
1028
- }).nativeToolSurfaceAllowed;
976
+ });
1029
977
  }
1030
978
  function resolveCodexRuntimePolicySessionKey(params, runtimeSessionKey) {
1031
979
  return runtimeSessionKey?.trim() || params.sandboxSessionKey?.trim() || params.sessionKey?.trim() || params.sessionId;
@@ -1088,8 +1036,8 @@ function disableCodexPluginThreadConfig(pluginConfig) {
1088
1036
  };
1089
1037
  }
1090
1038
  /** Adds sandbox_exec/process aliases when native Code Mode cannot directly honor the sandbox. */
1091
- function addSandboxShellDynamicToolsIfAvailable(filteredTools, allTools, input) {
1092
- if (!shouldExposeSandboxExecDynamicTool(input) || isSandboxShellDynamicToolExcluded(input.pluginConfig)) return filteredTools;
1039
+ function addSandboxShellDynamicToolsIfAvailable(filteredTools, allTools, input, executionPolicy) {
1040
+ if (isCodexMemoryFlushRun(input.params) || !executionPolicy.nativeToolSurfaceAllowed || !input.sandbox?.enabled || !input.sandbox.backendId.trim() || input.nativeToolSurfaceEnabled !== false || isSandboxShellDynamicToolExcluded(input.pluginConfig)) return filteredTools;
1093
1041
  const execTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "exec");
1094
1042
  const processTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "process");
1095
1043
  if (!execTool || !processTool) return filteredTools;
@@ -1116,16 +1064,6 @@ function addSandboxShellDynamicToolsIfAvailable(filteredTools, allTools, input)
1116
1064
  sandboxProcessTool
1117
1065
  ];
1118
1066
  }
1119
- function shouldExposeSandboxExecDynamicTool(input) {
1120
- if (isCodexMemoryFlushRun(input.params)) return false;
1121
- if (isCodexNativeExecutionBlockedByNodeExecHost(input.params, {
1122
- agentId: input.sessionAgentId,
1123
- runtimeSessionKey: input.sandboxSessionKey,
1124
- sandbox: input.sandbox
1125
- })) return false;
1126
- const backendId = input.sandbox?.enabled ? input.sandbox.backendId.trim().toLowerCase() : "";
1127
- return Boolean(backendId && input.nativeToolSurfaceEnabled === false);
1128
- }
1129
1067
  function isSandboxShellDynamicToolExcluded(config) {
1130
1068
  return isCodexDynamicToolExcluded(config, [
1131
1069
  "exec",
@@ -1134,32 +1072,24 @@ function isSandboxShellDynamicToolExcluded(config) {
1134
1072
  "sandbox_process"
1135
1073
  ]);
1136
1074
  }
1137
- function addNodeShellDynamicToolsIfNeeded(filteredTools, allTools, input, nodePolicy) {
1075
+ async function addNodeShellDynamicToolsIfNeeded(filteredTools, allTools, input, nodePolicy) {
1138
1076
  if (isCodexMemoryFlushRun(input.params)) return filteredTools;
1139
1077
  const nodeExecIsDefault = nodePolicy.effectiveExecHost === "node";
1140
1078
  const nodeExecAvailableFromAuto = nodePolicy.requestedExecHost === "auto" && nodePolicy.effectiveExecHost === "gateway";
1141
1079
  if (!nodeExecIsDefault && !nodeExecAvailableFromAuto) return filteredTools;
1142
1080
  const execTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "exec");
1143
- if (!execTool) return filteredTools;
1144
- if (!isCodexDynamicToolExcluded(input.pluginConfig, ["exec", "node_exec"]) && !filteredTools.some((tool) => normalizeCodexDynamicToolName(tool.name) === "node_exec")) return [...filteredTools, createExecAliasDynamicTool(execTool, {
1145
- host: "node",
1146
- node: nodePolicy.node
1147
- })];
1148
- return filteredTools;
1081
+ if (!execTool || isCodexDynamicToolExcluded(input.pluginConfig, ["exec", "node_exec"]) || filteredTools.some((tool) => normalizeCodexDynamicToolName(tool.name) === "node_exec")) return filteredTools;
1082
+ const nodeExec = await createNodeExecAliasDynamicTool(execTool, nodePolicy.node, input.runAbortController.signal, input.nodeExecAvailability);
1083
+ return nodeExec ? [...filteredTools, nodeExec] : filteredTools;
1149
1084
  }
1150
1085
  function shouldKeepOpenClawShellDynamicTools(input, nodePolicy) {
1151
1086
  return !isCodexMemoryFlushRun(input.params) && input.nativeToolSurfaceEnabled === false && input.sandbox?.enabled !== true && nodePolicy.effectiveExecHost !== "node";
1152
1087
  }
1153
- function resolveCodexNativeExecutionPolicyForDynamicTools(input) {
1154
- return resolveCodexNativeExecutionPolicy({
1155
- config: input.params.config,
1156
- sessionKey: resolveCodexRuntimePolicySessionKey(input.params, input.sandboxSessionKey),
1157
- sessionId: input.params.sessionId,
1158
- agentId: input.sessionAgentId,
1159
- execOverrides: input.params.execOverrides,
1160
- sandboxAvailable: input.sandbox?.enabled,
1161
- readRuntimeSessionEntry: true
1162
- });
1088
+ /** Keeps replacement shell tools direct even when model metadata mandates Codex Code Mode. */
1089
+ function placeDisabledNativeShellToolsInDirectNamespace(tools, nativeToolSurfaceEnabled) {
1090
+ if (nativeToolSurfaceEnabled !== false) return tools;
1091
+ for (const tool of tools) if (CODEX_DISABLED_NATIVE_SHELL_DYNAMIC_TOOLS.has(normalizeCodexDynamicToolName(tool.name))) tool.catalogMode = "direct-only";
1092
+ return tools;
1163
1093
  }
1164
1094
  /** Applies a normalized tool allowlist while preserving shell aliases for exec/process. */
1165
1095
  function filterCodexDynamicToolsForAllowlist(tools, toolsAllow) {
@@ -1182,9 +1112,9 @@ function shouldForceMessageTool(params) {
1182
1112
  }
1183
1113
  //#endregion
1184
1114
  //#region extensions/codex/src/app-server/sandbox-exec-server-node-relay.ts
1185
- const CODEX_NODE_EXEC_SERVER_MAX_MESSAGE_BYTES$1 = 64 * 1024 * 1024;
1115
+ const CODEX_NODE_EXEC_SERVER_MAX_MESSAGE_BYTES$1 = 67108864;
1186
1116
  const CODEX_NODE_EXEC_SERVER_MAX_FAILURE_DETAIL_CHARS = 240;
1187
- const CODEX_NODE_HTTP_CREDENTIAL_BODY_MAX_BYTES = 1024 * 1024;
1117
+ const CODEX_NODE_HTTP_CREDENTIAL_BODY_MAX_BYTES = 1048576;
1188
1118
  const CODEX_NODE_HTTP_CREDENTIAL_SCAN_MAX_FIELDS = 256;
1189
1119
  const CODEX_NODE_HTTP_CREDENTIAL_SCAN_MAX_DEPTH = 8;
1190
1120
  const CODEX_NODE_HTTP_CREDENTIAL_HEADER_NAMES = /* @__PURE__ */ new Set([
@@ -1824,10 +1754,10 @@ function joinSandboxChildPath(parent, child) {
1824
1754
  * Implements filesystem JSON-RPC handlers for the Codex sandbox exec-server
1825
1755
  * with OpenClaw sandbox policy checks before every bridge operation.
1826
1756
  */
1827
- const CODEX_SANDBOX_EXEC_SERVER_MAX_READ_FILE_BYTES = 512 * 1024 * 1024;
1757
+ const CODEX_SANDBOX_EXEC_SERVER_MAX_READ_FILE_BYTES = 536870912;
1828
1758
  const CODEX_SANDBOX_EXEC_SERVER_MAX_OPEN_FILE_READS = 128;
1829
- const CODEX_SANDBOX_EXEC_SERVER_MAX_BUFFERED_FILE_READ_BYTES = 64 * 1024 * 1024;
1830
- 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;
1831
1761
  const CODEX_SANDBOX_EXEC_SERVER_MAX_FILE_READ_HANDLE_ID_BYTES = 32;
1832
1762
  /** Opens a policy-checked sandbox file under a bounded, connection-owned handle. */
1833
1763
  async function openFile(execServer, handles, params) {
@@ -2231,7 +2161,7 @@ function delay(ms) {
2231
2161
  * access through the active OpenClaw sandbox backend.
2232
2162
  */
2233
2163
  /** Maximum JSON-line size accepted from the streaming HTTP helper process. */
2234
- const SANDBOX_HTTP_STREAM_LINE_MAX_CHARS = 256 * 1024;
2164
+ const SANDBOX_HTTP_STREAM_LINE_MAX_CHARS = 262144;
2235
2165
  /** Handles one sandbox HTTP JSON-RPC request, optionally streaming response body deltas. */
2236
2166
  async function httpRequest(execServer, notifications, params) {
2237
2167
  const record = requireObject(params, "http/request params");
@@ -2543,6 +2473,9 @@ def assert_url_allowed(url):
2543
2473
  PINNED_ADDRESSES[hostname] = sorted(addresses)
2544
2474
 
2545
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
+
2546
2479
  def __init__(self, redirect_policy):
2547
2480
  self.redirect_policy = redirect_policy
2548
2481
 
@@ -2656,7 +2589,7 @@ python3 "$tmp"
2656
2589
  * termination for Codex sandbox exec-server process RPCs.
2657
2590
  */
2658
2591
  const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
2659
- const RETAINED_PROCESS_OUTPUT_BYTES = 1024 * 1024;
2592
+ const RETAINED_PROCESS_OUTPUT_BYTES = 1048576;
2660
2593
  const CLOSED_PROCESS_EVICTION_MS = 6e4;
2661
2594
  /** Starts a sandbox-backed process and registers it in the connection-local process table. */
2662
2595
  async function startProcess(execServer, processes, notify, params) {
@@ -2913,10 +2846,11 @@ function readEnv(value) {
2913
2846
  return env;
2914
2847
  }
2915
2848
  function readProcessEnv(record) {
2916
- return sanitizeEnvVars({
2849
+ const requestedEnv = {
2917
2850
  ...buildEnvFromPolicy(record.envPolicy),
2918
2851
  ...readEnv(record.env)
2919
- }).allowed;
2852
+ };
2853
+ return sanitizeEnvVars(requestedEnv).allowed;
2920
2854
  }
2921
2855
  function buildEnvFromPolicy(value) {
2922
2856
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
@@ -3031,8 +2965,8 @@ var CodexSandboxExecSession = class {
3031
2965
  * Hosts the local OpenClaw sandbox exec-server that Codex app-server native
3032
2966
  * execution can register as an external environment.
3033
2967
  */
3034
- const CODEX_SANDBOX_EXEC_SERVER_MAX_INBOUND_MESSAGE_BYTES = 100 * 1024 * 1024;
3035
- 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;
3036
2970
  const codexNodeExecServerLeases = /* @__PURE__ */ new WeakMap();
3037
2971
  /** Starts or reuses a sandbox exec-server and registers it with Codex app-server. */
3038
2972
  async function ensureCodexSandboxExecServerEnvironment(params) {
@@ -3323,52 +3257,6 @@ async function handleMessage(session, data) {
3323
3257
  await session.handleRequest(parseRequest(buffer.toString("utf8")));
3324
3258
  }
3325
3259
  //#endregion
3326
- //#region extensions/codex/src/app-server/provider-capabilities.ts
3327
- function resolveOverriddenProviderWebSearchSupport(modelProviderOverride) {
3328
- const provider = modelProviderOverride?.trim().toLowerCase();
3329
- if (!provider) return;
3330
- return provider === "openai" ? "supported" : "unsupported";
3331
- }
3332
- async function readConfiguredProviderWebSearchSupport(params) {
3333
- return (await params.client.request("modelProvider/capabilities/read", {}, {
3334
- timeoutMs: params.timeoutMs,
3335
- signal: params.signal
3336
- })).webSearch ? "supported" : "unsupported";
3337
- }
3338
- async function resolveCodexProviderWebSearchSupportForClient(params) {
3339
- const overrideSupport = resolveOverriddenProviderWebSearchSupport(params.modelProviderOverride);
3340
- if (overrideSupport) return overrideSupport;
3341
- try {
3342
- return await readConfiguredProviderWebSearchSupport(params);
3343
- } catch {
3344
- return "unknown";
3345
- }
3346
- }
3347
- async function resolveCodexProviderWebSearchSupport(params) {
3348
- const overrideSupport = resolveOverriddenProviderWebSearchSupport(params.modelProviderOverride);
3349
- if (overrideSupport) return overrideSupport;
3350
- let client;
3351
- try {
3352
- client = await params.clientFactory({
3353
- startOptions: params.appServer.start,
3354
- ...params.preparedAuth ? { preparedAuth: params.preparedAuth } : { authProfileId: params.authProfileId },
3355
- agentDir: params.agentDir,
3356
- config: params.config,
3357
- timeoutMs: params.appServer.requestTimeoutMs
3358
- });
3359
- return await resolveCodexProviderWebSearchSupportForClient({
3360
- client,
3361
- timeoutMs: params.appServer.requestTimeoutMs,
3362
- modelProviderOverride: params.modelProviderOverride,
3363
- signal: params.signal
3364
- });
3365
- } catch {
3366
- return "unknown";
3367
- } finally {
3368
- if (client) releaseLeasedSharedCodexAppServerClient(client);
3369
- }
3370
- }
3371
- //#endregion
3372
3260
  //#region extensions/codex/src/app-server/approval-bridge.ts
3373
3261
  /**
3374
3262
  * Bridges Codex app-server approval requests into OpenClaw policy hooks and
@@ -3413,6 +3301,8 @@ async function handleCodexAppServerApprovalRequest(params) {
3413
3301
  resolvedOutcome = "approved-once";
3414
3302
  resolvedMessage = "Codex app-server approval granted for this byte-bound command only.";
3415
3303
  }
3304
+ params.signal?.throwIfAborted();
3305
+ if (resolvedOutcome !== "denied") params.paramsForRun.hostCapabilities.assertActive();
3416
3306
  emitApprovalEvent(params.paramsForRun, {
3417
3307
  phase: "resolved",
3418
3308
  kind: context.kind,
@@ -3451,6 +3341,7 @@ async function handleCodexAppServerApprovalRequest(params) {
3451
3341
  autoApprove: params.autoApprove,
3452
3342
  signal: params.signal
3453
3343
  });
3344
+ params.signal?.throwIfAborted();
3454
3345
  if (policyOutcome?.outcome === "denied") {
3455
3346
  recordNativeToolFailureDisposition(params, context, policyOutcome.failureDisposition);
3456
3347
  return await resolvePolicyApproval("denied", policyOutcome.reason);
@@ -3459,6 +3350,7 @@ async function handleCodexAppServerApprovalRequest(params) {
3459
3350
  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.");
3460
3351
  const requestResult = await requestPluginApproval({
3461
3352
  hostCapabilities: params.paramsForRun.hostCapabilities,
3353
+ signal: params.signal,
3462
3354
  title: context.title,
3463
3355
  description: context.description,
3464
3356
  severity: context.severity,
@@ -3972,7 +3864,8 @@ function summarizeNetworkPolicyAmendments(value) {
3972
3864
  function sanitizePermissionHostValue(value) {
3973
3865
  const withoutScheme = sanitizePermissionScalar(value).toLowerCase().replace(/^[a-z][a-z0-9+.-]*:\/\//, "");
3974
3866
  const authority = withoutScheme.split(/[/?#]/, 1)[0] ?? withoutScheme;
3975
- return truncateCodexApprovalDisplayText(authority.includes("@") ? authority.slice(authority.lastIndexOf("@") + 1) : authority, PERMISSION_VALUE_MAX_LENGTH);
3867
+ const withoutUserInfo = authority.includes("@") ? authority.slice(authority.lastIndexOf("@") + 1) : authority;
3868
+ return truncateCodexApprovalDisplayText(withoutUserInfo, PERMISSION_VALUE_MAX_LENGTH);
3976
3869
  }
3977
3870
  function sanitizePermissionPathValue(value) {
3978
3871
  return truncateCodexApprovalDisplayText(formatApprovalDisplayPath(sanitizePermissionScalar(value)), PERMISSION_VALUE_MAX_LENGTH);
@@ -4101,7 +3994,8 @@ function appendPreviewPart(source, part, separator) {
4101
3994
  }
4102
3995
  function sanitizeApprovalPreview(source, maxLength) {
4103
3996
  if (!source || !source.value) return { omitted: false };
4104
- const sanitized = sanitizeCodexApprovalVisibleText(stripDanglingCodexApprovalTerminalSequence(source.value));
3997
+ const rawPreview = stripDanglingCodexApprovalTerminalSequence(source.value);
3998
+ const sanitized = sanitizeCodexApprovalVisibleText(rawPreview);
4105
3999
  if (!sanitized) return { omitted: true };
4106
4000
  return {
4107
4001
  text: formatCodexDisplayText(truncateCodexApprovalDisplayText(sanitized, maxLength)),
@@ -4246,6 +4140,7 @@ async function routeCodexAppServerElicitationRequest(params) {
4246
4140
  const requestTurnId = requestParams.turnId;
4247
4141
  if (requestTurnId !== null && requestTurnId !== void 0 && requestTurnId !== params.turnId) return { kind: "not-mine" };
4248
4142
  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" };
4143
+ if (params.signal?.aborted) return handled(createCodexElicitationResponse("cancel"));
4249
4144
  const pluginResolution = resolvePluginElicitation({
4250
4145
  requestParams,
4251
4146
  pluginAppPolicyContext: params.pluginAppPolicyContext
@@ -4270,16 +4165,63 @@ async function routeCodexAppServerElicitationRequest(params) {
4270
4165
  signal: params.signal
4271
4166
  }));
4272
4167
  }
4273
- const approvalPrompt = readComputerUseApprovalElicitation(requestParams, params.computerUseMcpServerName) ?? readBridgeableApprovalElicitation(requestParams);
4168
+ const computerUsePrompt = readComputerUseApprovalElicitation(requestParams, params.computerUseMcpServerName);
4169
+ const approvalPrompt = computerUsePrompt ?? readBridgeableApprovalElicitation(requestParams);
4274
4170
  if (!approvalPrompt) return handled(createCodexElicitationResponse("decline"));
4171
+ let persistence;
4172
+ if (!computerUsePrompt) {
4173
+ const serverName = readNonBlankStringField(requestParams, "serverName");
4174
+ const server = serverName ? params.paramsForRun.config?.mcp?.servers?.[serverName] : void 0;
4175
+ const mode = serverName ? resolveProjectedMcpCodexToolApprovalMode(serverName, server ?? {}, params.projectedMcpServers?.[serverName]) : void 0;
4176
+ if (!requiresMcpCodexToolApproval({
4177
+ mode,
4178
+ fullPermission: params.autoApproveMcpTools
4179
+ })) {
4180
+ params.paramsForRun.hostCapabilities.assertActive();
4181
+ return handled(buildElicitationResponse(approvalPrompt, "approved-once"));
4182
+ }
4183
+ if (mode === "prompt") approvalPrompt.allowedDecisions = ["allow-once", "deny"];
4184
+ else if (serverName && serverName !== CODEX_APPS_SERVER_NAME && Object.hasOwn(params.paramsForRun.config?.mcp?.servers ?? {}, serverName) && requestTurnId === params.turnId && readPersistHints(approvalPrompt.meta, "explicit").includes("always")) {
4185
+ const resolveItem = () => {
4186
+ const item = params.getActiveMcpToolCall?.(serverName);
4187
+ return item?.server === serverName && matchesMcpApprovalDisplay(item, approvalPrompt.meta) ? item : void 0;
4188
+ };
4189
+ const item = resolveItem();
4190
+ if (item) persistence = {
4191
+ mcpTool: {
4192
+ server: serverName,
4193
+ tool: item.tool
4194
+ },
4195
+ toolCallId: item.id,
4196
+ isMcpToolApprovalActive: () => {
4197
+ const current = resolveItem();
4198
+ return current?.id === item.id && current.tool === item.tool;
4199
+ }
4200
+ };
4201
+ }
4202
+ }
4275
4203
  return handled(buildElicitationResponse(approvalPrompt, await requestPluginApprovalOutcome({
4276
- paramsForRun: params.paramsForRun,
4204
+ hostCapabilities: params.paramsForRun.hostCapabilities,
4277
4205
  title: approvalPrompt.title,
4278
4206
  description: approvalPrompt.description,
4279
4207
  allowedDecisions: approvalPrompt.allowedDecisions,
4208
+ toolName: "codex_mcp_tool_approval",
4209
+ ...persistence,
4280
4210
  signal: params.signal
4281
4211
  })));
4282
4212
  }
4213
+ function matchesMcpApprovalDisplay(item, meta) {
4214
+ if (!Object.hasOwn(meta, MCP_TOOL_APPROVAL_TOOL_PARAMS_DISPLAY_KEY)) return true;
4215
+ const display = meta[MCP_TOOL_APPROVAL_TOOL_PARAMS_DISPLAY_KEY];
4216
+ if (!Array.isArray(display)) return false;
4217
+ const args = item.arguments;
4218
+ return display.every((param) => {
4219
+ if (!isJsonObject(param) || typeof param.name !== "string" || !isJsonObject(args)) return false;
4220
+ if (!Object.hasOwn(args, param.name)) return false;
4221
+ const value = args[param.name];
4222
+ return typeof param.value !== "string" || param.value === (typeof value === "string" ? value : JSON.stringify(value));
4223
+ });
4224
+ }
4283
4225
  function handled(response) {
4284
4226
  return {
4285
4227
  kind: "handled",
@@ -4395,14 +4337,14 @@ async function buildPluginPolicyElicitationResponse(params) {
4395
4337
  const response = buildElicitationResponse(approvalPrompt, "approved-once");
4396
4338
  if (response.action === "accept") {
4397
4339
  if (mode === "allow") return response;
4398
- const outcome = await requestPluginApprovalOutcome({
4399
- paramsForRun: params.paramsForRun,
4340
+ return buildElicitationResponse(approvalPrompt, await requestPluginApprovalOutcome({
4341
+ hostCapabilities: params.paramsForRun.hostCapabilities,
4400
4342
  title: approvalPrompt.title,
4401
4343
  description: approvalPrompt.description,
4402
4344
  allowedDecisions: allowedPluginPolicyApprovalDecisions(mode, approvalPrompt),
4345
+ toolName: "codex_mcp_tool_approval",
4403
4346
  signal: params.signal
4404
- });
4405
- return buildElicitationResponse(approvalPrompt, mode === "ask" && outcome === "approved-session" ? "approved-once" : outcome);
4347
+ }));
4406
4348
  }
4407
4349
  logPluginElicitationDecline("unmappable_schema", params.requestParams);
4408
4350
  return createCodexElicitationResponse("decline");
@@ -4437,15 +4379,16 @@ function readPluginApprovalElicitation(entry, requestParams) {
4437
4379
  allowedDecisions: buildApprovalAllowedDecisions(requestedSchema, meta)
4438
4380
  };
4439
4381
  }
4440
- function buildApprovalAllowedDecisions(requestedSchema, meta) {
4441
- return canMapPersistentApproval(requestedSchema, meta) ? [
4382
+ function buildApprovalAllowedDecisions(requestedSchema, meta, allowSession = false) {
4383
+ return canMapPersistentApproval(requestedSchema, meta, allowSession) ? [
4442
4384
  "allow-once",
4443
4385
  "allow-always",
4444
4386
  "deny"
4445
4387
  ] : ["allow-once", "deny"];
4446
4388
  }
4447
- function canMapPersistentApproval(requestedSchema, meta) {
4389
+ function canMapPersistentApproval(requestedSchema, meta, allowSession) {
4448
4390
  const persistHints = readPersistHints(meta, "explicit");
4391
+ if (allowSession) return choosePersistHint(persistHints) !== void 0;
4449
4392
  if (persistHints.length > 0) return persistHints.includes("always");
4450
4393
  const properties = isJsonObject(requestedSchema.properties) ? requestedSchema.properties : {};
4451
4394
  return Object.entries(properties).some(([name, value]) => {
@@ -4470,16 +4413,20 @@ function readBridgeableApprovalElicitation(requestParams) {
4470
4413
  const requestedSchema = requestParams.requestedSchema;
4471
4414
  if (readNonBlankStringField(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
4472
4415
  const title = sanitizeDisplayText(readNonBlankStringField(requestParams, "message") ?? "") || "Codex MCP tool approval";
4416
+ const serverName = readNonBlankStringField(requestParams, "serverName");
4473
4417
  return {
4474
4418
  title,
4475
4419
  description: buildApprovalDescription({
4476
4420
  title,
4477
4421
  meta: requestParams["_meta"],
4478
4422
  requestedSchema,
4479
- serverName: sanitizeOptionalDisplayText(readNonBlankStringField(requestParams, "serverName"))
4423
+ serverName: sanitizeOptionalDisplayText(serverName),
4424
+ remedy: serverName ? formatMcpCodexApprovalRemedy(serverName) : void 0
4480
4425
  }),
4481
4426
  requestedSchema,
4482
- meta: requestParams["_meta"]
4427
+ meta: requestParams["_meta"],
4428
+ persistHintsMode: "explicit",
4429
+ allowedDecisions: buildApprovalAllowedDecisions(requestedSchema, requestParams["_meta"], true)
4483
4430
  };
4484
4431
  }
4485
4432
  function readComputerUseApprovalElicitation(requestParams, expectedServerName) {
@@ -4509,6 +4456,7 @@ function buildApprovalDescription(params) {
4509
4456
  connectorName && `App: ${connectorName}`,
4510
4457
  toolTitle && `Tool: ${toolTitle}`,
4511
4458
  params.serverName && `MCP server: ${params.serverName}`,
4459
+ params.remedy,
4512
4460
  toolDescription
4513
4461
  ].filter((line) => Boolean(line));
4514
4462
  const paramLines = readDisplayParamLines(params.meta);
@@ -4544,7 +4492,8 @@ function readDisplayParamLines(meta) {
4544
4492
  return remaining > 0 ? [...lines, `- Additional parameters: ${remaining} more`] : lines;
4545
4493
  }
4546
4494
  function formatDisplayParamValue(value) {
4547
- return truncateCodexApprovalDisplayText(sanitizeDisplayText(typeof value === "string" ? value : formatDisplayJsonValue(value ?? null)), MAX_DISPLAY_PARAM_VALUE_LENGTH);
4495
+ const formatted = typeof value === "string" ? value : formatDisplayJsonValue(value ?? null);
4496
+ return truncateCodexApprovalDisplayText(sanitizeDisplayText(formatted), MAX_DISPLAY_PARAM_VALUE_LENGTH);
4548
4497
  }
4549
4498
  function formatDisplayJsonValue(value, depth = MAX_DISPLAY_VALUE_DEPTH) {
4550
4499
  if (value === null) return "null";
@@ -4588,35 +4537,10 @@ function sanitizeDisplayText(value) {
4588
4537
  const escaped = sanitized ? formatCodexDisplayText(sanitized) : "";
4589
4538
  return clipped && escaped ? `${escaped}...` : escaped;
4590
4539
  }
4591
- async function requestPluginApprovalOutcome(params) {
4592
- try {
4593
- const requestResult = await requestPluginApproval({
4594
- hostCapabilities: params.paramsForRun.hostCapabilities,
4595
- title: params.title,
4596
- description: params.description,
4597
- severity: "warning",
4598
- toolName: "codex_mcp_tool_approval",
4599
- allowedDecisions: params.allowedDecisions
4600
- });
4601
- const approvalId = requestResult?.id;
4602
- if (!approvalId) return "unavailable";
4603
- const approvalResult = approvalRequestExplicitlyUnavailable(requestResult) ? void 0 : await waitForPluginApprovalDecision({
4604
- hostCapabilities: params.paramsForRun.hostCapabilities,
4605
- approvalId,
4606
- signal: params.signal
4607
- });
4608
- if (params.signal?.aborted) return "cancelled";
4609
- if (approvalResult?.terminalReason === "timeout") return "timed-out";
4610
- return mapExecDecisionToOutcome(approvalResult?.decision);
4611
- } catch {
4612
- return params.signal?.aborted ? "cancelled" : "denied";
4613
- }
4614
- }
4615
4540
  function buildElicitationResponse(approvalPrompt, outcome) {
4616
4541
  const { requestedSchema, meta } = approvalPrompt;
4617
4542
  if (outcome === "cancelled") return createCodexElicitationResponse("cancel");
4618
- if (outcome === "timed-out") return createCodexElicitationResponse("decline", null, { message: codexApprovalTimeoutText("other") });
4619
- if (outcome === "denied" || outcome === "unavailable") return createCodexElicitationResponse("decline");
4543
+ if (outcome === "timed-out" || outcome === "denied" || outcome === "unavailable") return createCodexElicitationResponse("decline");
4620
4544
  const content = buildAcceptedContent(approvalPrompt, outcome);
4621
4545
  if (!content && !hasNoSchemaProperties(requestedSchema)) {
4622
4546
  embeddedAgentLog.warn("codex MCP approval elicitation approved without a mappable response", {
@@ -4643,7 +4567,7 @@ function buildAcceptedContent(approvalPrompt, outcome) {
4643
4567
  schema,
4644
4568
  required: required.has(name)
4645
4569
  };
4646
- const next = readApprovalFieldValue(property, outcome) ?? readPersistFieldValue(property, meta, outcome, approvalPrompt.persistHintsMode ?? "legacy") ?? readFallbackFieldValue(property, outcome);
4570
+ const next = readApprovalFieldValue(property, outcome, choosePersistHint(readPersistHints(meta, approvalPrompt.persistHintsMode))) ?? readPersistFieldValue(property, meta, outcome, approvalPrompt.persistHintsMode ?? "legacy") ?? readFallbackFieldValue(property, outcome);
4647
4571
  if (next === void 0) {
4648
4572
  if (isApprovalField(property)) sawApprovalField = true;
4649
4573
  if (property.required) return;
@@ -4654,15 +4578,14 @@ function buildAcceptedContent(approvalPrompt, outcome) {
4654
4578
  }
4655
4579
  return sawApprovalField ? content : void 0;
4656
4580
  }
4657
- function readApprovalFieldValue(property, outcome) {
4581
+ function readApprovalFieldValue(property, outcome, persist) {
4658
4582
  if (!isApprovalField(property)) return;
4659
4583
  if (readNonBlankStringField(property.schema, "type") === "boolean") return true;
4660
4584
  const options = readEnumOptions(property.schema);
4661
4585
  if (options.length === 0) return;
4662
- const sessionChoice = options.find((option) => isSessionApprovalOption(option));
4663
4586
  const acceptChoice = options.find((option) => isPositiveApprovalOption(option));
4664
- if (outcome === "approved-session") return sessionChoice?.value ?? acceptChoice?.value;
4665
- return acceptChoice?.value ?? sessionChoice?.value;
4587
+ if (outcome === "approved-session") return options.find((option) => isPersistentApprovalOption(option, persist))?.value ?? acceptChoice?.value;
4588
+ return acceptChoice?.value;
4666
4589
  }
4667
4590
  function readPersistFieldValue(property, meta, outcome, persistHintsMode) {
4668
4591
  if (!isPersistField(property) || outcome !== "approved-session") return;
@@ -4741,9 +4664,9 @@ function isPositiveApprovalOption(option) {
4741
4664
  const haystack = `${option.value} ${option.label}`.toLowerCase();
4742
4665
  return /\b(allow|approve|accept|yes|continue|proceed|true)\b/.test(haystack);
4743
4666
  }
4744
- function isSessionApprovalOption(option) {
4667
+ function isPersistentApprovalOption(option, persist) {
4745
4668
  const haystack = `${option.value} ${option.label}`.toLowerCase();
4746
- return /\b(session|always|persistent)\b/.test(haystack) && /\b(allow|approve|accept)\b/.test(haystack);
4669
+ return (persist === "always" ? /\b(always|persistent)\b|\bdon't ask me again\b/.test(haystack) : persist === "session" && /\bsession\b/.test(haystack)) && /\b(allow|approve|accept)\b/.test(haystack);
4747
4670
  }
4748
4671
  function readNonBlankStringField(record, key) {
4749
4672
  const value = record?.[key];
@@ -4756,57 +4679,242 @@ function readFirstString$1(record, keys) {
4756
4679
  }
4757
4680
  }
4758
4681
  //#endregion
4759
- //#region extensions/codex/src/app-server/dynamic-tools.ts
4760
- /**
4761
- * Bridges OpenClaw runtime tools into Codex app-server dynamic tool specs and
4762
- * tool-call responses.
4763
- */
4764
- const INTERNAL_TOOL_EXECUTION_VALIDATION = Symbol.for("openclaw.internalToolExecutionValidation");
4765
- const MAX_CODEX_DYNAMIC_TOOL_VALIDATION_ERRORS = 4;
4766
- const MAX_CODEX_DYNAMIC_TOOL_VALIDATION_ERROR_CHARS = 160;
4767
- const CODEX_DYNAMIC_TOOL_VALIDATION_TRUNCATED_SUFFIX = " [detail truncated]";
4768
- function shouldValidateCodexDynamicToolInput(tool) {
4769
- return getPluginToolMeta(tool)?.mcp?.operation !== "tool";
4770
- }
4771
- function assertCodexDynamicToolInputMatchesSchema(params) {
4772
- const validation = validateJsonSchemaValue({
4773
- schema: params.schema,
4774
- cacheKey: `codex-dynamic-tool-input:${params.toolName}:${JSON.stringify(params.schema)}`,
4775
- value: params.value
4776
- });
4777
- if (validation.ok) return;
4778
- const visibleErrors = validation.errors.slice(0, MAX_CODEX_DYNAMIC_TOOL_VALIDATION_ERRORS);
4779
- const details = visibleErrors.map((error) => {
4780
- if (error.text.length <= MAX_CODEX_DYNAMIC_TOOL_VALIDATION_ERROR_CHARS) return error.text;
4781
- return `${error.text.slice(0, MAX_CODEX_DYNAMIC_TOOL_VALIDATION_ERROR_CHARS - 19)}${CODEX_DYNAMIC_TOOL_VALIDATION_TRUNCATED_SUFFIX}`;
4782
- }).join("; ");
4783
- const omitted = validation.errors.length - visibleErrors.length;
4784
- const omittedSuffix = omitted > 0 ? `; ${omitted} more violation(s) omitted` : "";
4785
- throw new Error(`Invalid arguments for tool "${params.toolName}": ${details}${omittedSuffix}.`);
4786
- }
4787
- function createCodexDynamicToolValidationControl(params) {
4788
- return {
4789
- [INTERNAL_TOOL_EXECUTION_VALIDATION]: true,
4790
- toolCallId: params.toolCallId,
4791
- validate: params.validate
4792
- };
4793
- }
4794
- function applyCurrentMessageProvider(toolName, args, currentProvider) {
4795
- const hasProvider = typeof args.provider === "string" && args.provider.trim().length > 0 ? true : typeof args.channel === "string" && args.channel.trim().length > 0;
4796
- const provider = currentProvider?.trim();
4797
- if (toolName !== "message" || hasProvider || !provider) return args;
4798
- return {
4799
- ...args,
4800
- provider
4801
- };
4802
- }
4803
- function normalizeRouteToken(value) {
4804
- if (typeof value === "number") return Number.isFinite(value) ? String(value) : void 0;
4805
- const normalized = value?.trim().toLowerCase();
4806
- return normalized ? normalized : void 0;
4807
- }
4808
- function sourceRouteTokens(hookContext) {
4809
- const tokens = /* @__PURE__ */ new Set();
4682
+ //#region extensions/codex/src/app-server/dynamic-tool-catalog.ts
4683
+ /** Namespace attached to OpenClaw-owned dynamic tools exposed to Codex. */
4684
+ const CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE = "openclaw";
4685
+ const CODEX_DYNAMIC_TOOL_NAME_MAX_CHARS = 128;
4686
+ const CODEX_DYNAMIC_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/u;
4687
+ const ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES = /* @__PURE__ */ new Set([
4688
+ "agents_list",
4689
+ "progress_card",
4690
+ "sessions_spawn",
4691
+ "sessions_yield"
4692
+ ]);
4693
+ function createCodexDynamicToolSpecs(params) {
4694
+ const directToolNames = /* @__PURE__ */ new Set([...ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES, ...params.directToolNames ?? []]);
4695
+ const specs = [];
4696
+ const namespaceTools = [];
4697
+ const directOnlyNamespaceTools = [];
4698
+ const entries = params.loading === "direct" ? params.entries : params.entries.toSorted((left, right) => left.name.localeCompare(right.name));
4699
+ for (const entry of entries) {
4700
+ const functionSpec = createCodexDynamicToolFunctionSpec({ entry });
4701
+ if (entry.name === "openclaw" && directToolNames.has(entry.name)) {
4702
+ specs.push(functionSpec);
4703
+ continue;
4704
+ }
4705
+ if (entry.tool.catalogMode === "direct-only") {
4706
+ directOnlyNamespaceTools.push(functionSpec);
4707
+ continue;
4708
+ }
4709
+ if (params.loading === "direct" || directToolNames.has(entry.name)) {
4710
+ specs.push(functionSpec);
4711
+ continue;
4712
+ }
4713
+ namespaceTools.push({
4714
+ ...functionSpec,
4715
+ deferLoading: true
4716
+ });
4717
+ }
4718
+ if (namespaceTools.length > 0) specs.push({
4719
+ type: "namespace",
4720
+ name: CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE,
4721
+ description: "",
4722
+ tools: namespaceTools
4723
+ });
4724
+ if (directOnlyNamespaceTools.length > 0) specs.push({
4725
+ type: "namespace",
4726
+ name: CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE,
4727
+ description: "",
4728
+ tools: directOnlyNamespaceTools
4729
+ });
4730
+ return specs;
4731
+ }
4732
+ function createCodexDynamicToolFunctionSpec(params) {
4733
+ return {
4734
+ type: "function",
4735
+ name: params.entry.name,
4736
+ description: params.entry.description,
4737
+ inputSchema: params.entry.inputSchema
4738
+ };
4739
+ }
4740
+ function projectCodexDynamicTools(tools) {
4741
+ const projectedTools = [];
4742
+ const quarantinedTools = [];
4743
+ let length;
4744
+ try {
4745
+ length = tools.length;
4746
+ } catch {
4747
+ return {
4748
+ tools: [],
4749
+ quarantinedTools: [{
4750
+ tool: "tool[0]",
4751
+ violations: ["tool[0] is unreadable"]
4752
+ }]
4753
+ };
4754
+ }
4755
+ for (let toolIndex = 0; toolIndex < length; toolIndex += 1) {
4756
+ let tool;
4757
+ try {
4758
+ tool = tools[toolIndex];
4759
+ } catch {
4760
+ quarantinedTools.push({
4761
+ tool: `tool[${toolIndex}]`,
4762
+ violations: [`tool[${toolIndex}] is unreadable`]
4763
+ });
4764
+ continue;
4765
+ }
4766
+ const descriptor = readCodexDynamicToolDescriptor(tool, toolIndex);
4767
+ if (!descriptor.ok) {
4768
+ quarantinedTools.push(descriptor.diagnostic);
4769
+ continue;
4770
+ }
4771
+ const normalizedParameters = normalizeOpenAIStrictCompatSchema(descriptor.parameters);
4772
+ const projection = projectRuntimeToolInputSchema(normalizedParameters ?? descriptor.parameters, `${descriptor.name}.inputSchema`);
4773
+ if (projection.violations.length > 0) {
4774
+ quarantinedTools.push({
4775
+ tool: descriptor.name,
4776
+ violations: projection.violations
4777
+ });
4778
+ continue;
4779
+ }
4780
+ if (!isRecord(projection.schema)) {
4781
+ quarantinedTools.push({
4782
+ tool: descriptor.name,
4783
+ violations: [`${descriptor.name}.inputSchema must be a JSON object schema`]
4784
+ });
4785
+ continue;
4786
+ }
4787
+ projectedTools.push({
4788
+ tool,
4789
+ name: descriptor.name,
4790
+ description: descriptor.description,
4791
+ inputSchema: projection.schema
4792
+ });
4793
+ }
4794
+ return {
4795
+ tools: projectedTools,
4796
+ quarantinedTools
4797
+ };
4798
+ }
4799
+ function readCodexDynamicToolDescriptor(tool, toolIndex) {
4800
+ const fallbackName = `tool[${toolIndex}]`;
4801
+ let name;
4802
+ try {
4803
+ const rawName = tool.name;
4804
+ if (typeof rawName !== "string" || !rawName) return {
4805
+ ok: false,
4806
+ diagnostic: {
4807
+ tool: fallbackName,
4808
+ violations: [`${fallbackName}.name must be a non-empty string`]
4809
+ }
4810
+ };
4811
+ const trimmedName = rawName.trim();
4812
+ let nameViolation;
4813
+ if (!trimmedName) nameViolation = `${rawName}.name must not be empty`;
4814
+ else if (trimmedName !== rawName) nameViolation = `${rawName}.name must not have leading or trailing whitespace`;
4815
+ else if (!CODEX_DYNAMIC_TOOL_NAME_PATTERN.test(rawName)) nameViolation = `${rawName}.name must match ^[a-zA-Z0-9_-]+$`;
4816
+ else if (rawName.length > CODEX_DYNAMIC_TOOL_NAME_MAX_CHARS) nameViolation = `${rawName}.name must be at most ${CODEX_DYNAMIC_TOOL_NAME_MAX_CHARS} characters`;
4817
+ else if (rawName === "mcp" || rawName.startsWith("mcp__")) nameViolation = `${rawName}.name is reserved by Codex app-server`;
4818
+ if (nameViolation) return {
4819
+ ok: false,
4820
+ diagnostic: {
4821
+ tool: rawName,
4822
+ violations: [nameViolation]
4823
+ }
4824
+ };
4825
+ name = rawName;
4826
+ } catch {
4827
+ return {
4828
+ ok: false,
4829
+ diagnostic: {
4830
+ tool: fallbackName,
4831
+ violations: [`${fallbackName}.name is unreadable`]
4832
+ }
4833
+ };
4834
+ }
4835
+ let description;
4836
+ try {
4837
+ description = typeof tool.description === "string" ? tool.description : "";
4838
+ } catch {
4839
+ return {
4840
+ ok: false,
4841
+ diagnostic: {
4842
+ tool: name,
4843
+ violations: [`${name}.description is unreadable`]
4844
+ }
4845
+ };
4846
+ }
4847
+ let parameters;
4848
+ try {
4849
+ parameters = tool.parameters;
4850
+ } catch {
4851
+ return {
4852
+ ok: false,
4853
+ diagnostic: {
4854
+ tool: name,
4855
+ violations: [`${name}.inputSchema is unreadable`]
4856
+ }
4857
+ };
4858
+ }
4859
+ return {
4860
+ ok: true,
4861
+ name,
4862
+ description,
4863
+ parameters
4864
+ };
4865
+ }
4866
+ //#endregion
4867
+ //#region extensions/codex/src/app-server/dynamic-tools.ts
4868
+ /**
4869
+ * Bridges OpenClaw runtime tools into Codex app-server dynamic tool specs and
4870
+ * tool-call responses.
4871
+ */
4872
+ const INTERNAL_TOOL_EXECUTION_VALIDATION = Symbol.for("openclaw.internalToolExecutionValidation");
4873
+ const MAX_CODEX_DYNAMIC_TOOL_VALIDATION_ERRORS = 4;
4874
+ const MAX_CODEX_DYNAMIC_TOOL_VALIDATION_ERROR_CHARS = 160;
4875
+ const CODEX_DYNAMIC_TOOL_VALIDATION_TRUNCATED_SUFFIX = " [detail truncated]";
4876
+ function shouldValidateCodexDynamicToolInput(tool) {
4877
+ return getPluginToolMeta(tool)?.mcp?.operation !== "tool";
4878
+ }
4879
+ function assertCodexDynamicToolInputMatchesSchema(params) {
4880
+ const validation = validateJsonSchemaValue({
4881
+ schema: params.schema,
4882
+ cacheKey: `codex-dynamic-tool-input:${params.toolName}:${JSON.stringify(params.schema)}`,
4883
+ value: params.value
4884
+ });
4885
+ if (validation.ok) return;
4886
+ const visibleErrors = validation.errors.slice(0, MAX_CODEX_DYNAMIC_TOOL_VALIDATION_ERRORS);
4887
+ const details = visibleErrors.map((error) => {
4888
+ if (error.text.length <= MAX_CODEX_DYNAMIC_TOOL_VALIDATION_ERROR_CHARS) return error.text;
4889
+ return `${error.text.slice(0, 141)}${CODEX_DYNAMIC_TOOL_VALIDATION_TRUNCATED_SUFFIX}`;
4890
+ }).join("; ");
4891
+ const omitted = validation.errors.length - visibleErrors.length;
4892
+ const omittedSuffix = omitted > 0 ? `; ${omitted} more violation(s) omitted` : "";
4893
+ throw new Error(`Invalid arguments for tool "${params.toolName}": ${details}${omittedSuffix}.`);
4894
+ }
4895
+ function createCodexDynamicToolValidationControl(params) {
4896
+ return {
4897
+ [INTERNAL_TOOL_EXECUTION_VALIDATION]: true,
4898
+ toolCallId: params.toolCallId,
4899
+ validate: params.validate
4900
+ };
4901
+ }
4902
+ function applyCurrentMessageProvider(toolName, args, currentProvider) {
4903
+ const hasProvider = typeof args.provider === "string" && args.provider.trim().length > 0 ? true : typeof args.channel === "string" && args.channel.trim().length > 0;
4904
+ const provider = currentProvider?.trim();
4905
+ if (toolName !== "message" || hasProvider || !provider) return args;
4906
+ return {
4907
+ ...args,
4908
+ provider
4909
+ };
4910
+ }
4911
+ function normalizeRouteToken(value) {
4912
+ if (typeof value === "number") return Number.isFinite(value) ? String(value) : void 0;
4913
+ const normalized = value?.trim().toLowerCase();
4914
+ return normalized ? normalized : void 0;
4915
+ }
4916
+ function sourceRouteTokens(hookContext) {
4917
+ const tokens = /* @__PURE__ */ new Set();
4810
4918
  const currentTarget = normalizeRouteToken(hookContext?.currentMessagingTarget);
4811
4919
  const currentChannel = normalizeRouteToken(hookContext?.currentChannelId);
4812
4920
  const currentProvider = normalizeRouteToken(hookContext?.currentChannelProvider);
@@ -4919,15 +5027,6 @@ function normalizeAcceptedSessionSpawn(result) {
4919
5027
  childSessionKey
4920
5028
  } : null;
4921
5029
  }
4922
- /** Namespace attached to OpenClaw-owned dynamic tools exposed to Codex. */
4923
- const CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE = "openclaw";
4924
- const CODEX_DYNAMIC_TOOL_NAME_MAX_CHARS = 128;
4925
- const CODEX_DYNAMIC_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/u;
4926
- const ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES = /* @__PURE__ */ new Set([
4927
- "agents_list",
4928
- "sessions_spawn",
4929
- "sessions_yield"
4930
- ]);
4931
5030
  const EXPLICIT_MESSAGE_PROVIDER_KEYS = ["channel", "provider"];
4932
5031
  const EXPLICIT_MESSAGE_TARGET_KEYS = [
4933
5032
  "target",
@@ -4967,11 +5066,13 @@ function createCodexDynamicToolBridge(params) {
4967
5066
  const toolResultMaxChars = typeof contextWindowTokens === "number" && Number.isFinite(contextWindowTokens) && contextWindowTokens > 0 ? Math.max(1, resolveLiveToolResultMaxChars({ contextWindowTokens })) : DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS;
4968
5067
  const availableProjection = projectCodexExecutableDynamicToolSurface(params.tools, params.hookContext);
4969
5068
  const registeredProjection = params.registeredTools ? projectCodexDynamicTools(params.registeredTools) : availableProjection;
4970
- const availableTools = availableProjection.tools;
5069
+ const inheritedSpecs = params.registeredSpecs ? structuredClone([...params.registeredSpecs]) : void 0;
5070
+ const inheritedNames = inheritedSpecs ? new Set(flattenCodexDynamicToolFunctions(inheritedSpecs).map((tool) => tool.name)) : void 0;
5071
+ const availableTools = availableProjection.tools.filter((entry) => !inheritedNames || inheritedNames.has(entry.name));
4971
5072
  const quarantinedAvailableToolNames = new Set(availableProjection.quarantinedTools.map((tool) => tool.tool));
4972
5073
  const registeredSpecTools = (params.registeredTools ? registeredProjection.tools : availableTools).filter((entry) => !quarantinedAvailableToolNames.has(entry.name));
4973
5074
  const toolMap = new Map(availableTools.map((entry) => [entry.name, entry]));
4974
- const registeredToolNames = new Set(registeredSpecTools.map((entry) => entry.name));
5075
+ const registeredToolNames = inheritedNames ?? new Set(registeredSpecTools.map((entry) => entry.name));
4975
5076
  const quarantinedTools = dedupeQuarantinedDynamicTools([...availableProjection.quarantinedTools, ...registeredProjection.quarantinedTools]);
4976
5077
  warnQuarantinedDynamicTools({
4977
5078
  tools: quarantinedTools,
@@ -4987,6 +5088,8 @@ function createCodexDynamicToolBridge(params) {
4987
5088
  messagingToolSentTargets: [],
4988
5089
  messagingToolSourceReplyPayloads: [],
4989
5090
  toolMediaUrls: [],
5091
+ toolAutoDeliveryMediaUrls: [],
5092
+ coreTtsToolResults: [],
4990
5093
  toolAudioAsVoice: false,
4991
5094
  acceptedSessionSpawns: [],
4992
5095
  quarantinedTools
@@ -5002,16 +5105,23 @@ function createCodexDynamicToolBridge(params) {
5002
5105
  };
5003
5106
  const legacyExtensionRunner = createCodexAppServerToolResultExtensionRunner(toolResultHookContext);
5004
5107
  const executionSnapshotStates = /* @__PURE__ */ new Map();
5005
- const directToolNames = /* @__PURE__ */ new Set([...ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES, ...params.directToolNames ?? []]);
5108
+ const directToolNames = params.directToolNames;
5006
5109
  let readRemoteWorkspaceFile;
5007
5110
  return {
5008
5111
  availableTools: availableTools.map((entry) => entry.tool),
5009
- availableSpecs: createCodexDynamicToolSpecs({
5112
+ availableSpecs: inheritedSpecs ? inheritedSpecs.flatMap((spec) => {
5113
+ if (spec.type === "function") return toolMap.has(spec.name) ? [spec] : [];
5114
+ const tools = spec.tools.filter((tool) => toolMap.has(tool.name));
5115
+ return tools.length ? [{
5116
+ ...spec,
5117
+ tools
5118
+ }] : [];
5119
+ }) : createCodexDynamicToolSpecs({
5010
5120
  entries: availableTools,
5011
5121
  loading: params.loading ?? "searchable",
5012
5122
  directToolNames
5013
5123
  }),
5014
- specs: createCodexDynamicToolSpecs({
5124
+ specs: inheritedSpecs ?? createCodexDynamicToolSpecs({
5015
5125
  entries: registeredSpecTools,
5016
5126
  loading: params.loading ?? "searchable",
5017
5127
  directToolNames
@@ -5200,13 +5310,17 @@ function createCodexDynamicToolBridge(params) {
5200
5310
  const toolConfirmedSourceReply = params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && !resultIsError && (rawResult.terminate === true || result.terminate === true);
5201
5311
  const confirmedSourceReply = params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && (toolConfirmedSourceReply || deliveredSourceReply || receiptConfirmedSourceReply);
5202
5312
  const sourceReplyFinal = confirmedSourceReply ? executedArgs.final !== false : void 0;
5313
+ const autoDeliveryTtsMediaUrls = getCoreTtsToolResultMediaUrls(rawResult);
5203
5314
  collectToolTelemetry({
5204
5315
  toolName,
5205
5316
  args: executedArgs,
5206
5317
  result,
5207
5318
  mediaTrustResult: telemetryRawResult,
5208
5319
  telemetry,
5320
+ signal,
5209
5321
  isError: resultIsError,
5322
+ autoDeliveryTtsMediaUrls,
5323
+ coreTtsToolResult: autoDeliveryTtsMediaUrls?.length ? rawResult : void 0,
5210
5324
  messagingTarget: confirmedMessagingTarget,
5211
5325
  sourceReplyFinal
5212
5326
  });
@@ -5222,6 +5336,8 @@ function createCodexDynamicToolBridge(params) {
5222
5336
  sideEffectEvidence: !replaySafe
5223
5337
  });
5224
5338
  } catch (error) {
5339
+ const trustedNoStart = consumeTrustedToolNoStartError(error);
5340
+ executionPrevented ||= trustedNoStart;
5225
5341
  captureExecutionBoundary();
5226
5342
  if (toolName === "computer" && params.computerContextEpoch?.frameToolCallId === call.callId) invalidateComputerFrame(params.computerContextEpoch);
5227
5343
  const executionDisposition = getBeforeToolCallFailureDisposition(error) ?? (signal.aborted ? resolveCodexToolAbortTerminalReason(signal) : resolveToolExecutionErrorKind(error));
@@ -5238,13 +5354,6 @@ function createCodexDynamicToolBridge(params) {
5238
5354
  toolCallOrdinal: options?.toolCallOrdinal
5239
5355
  });
5240
5356
  notifyAgentToolResult(options?.onAgentToolResult, toolName, failedResult, true);
5241
- collectToolTelemetry({
5242
- toolName,
5243
- args: executedArgs,
5244
- result: void 0,
5245
- telemetry,
5246
- isError: true
5247
- });
5248
5357
  runAgentHarnessAfterToolCallHook({
5249
5358
  toolName,
5250
5359
  toolCallId: call.callId,
@@ -5339,182 +5448,6 @@ function wrapProjectedCodexDynamicTools(tools, hookContext) {
5339
5448
  quarantinedTools
5340
5449
  };
5341
5450
  }
5342
- function createCodexDynamicToolSpecs(params) {
5343
- const specs = [];
5344
- const namespaceTools = [];
5345
- const directOnlyNamespaceTools = [];
5346
- const entries = params.loading === "direct" ? params.entries : params.entries.toSorted((left, right) => left.name.localeCompare(right.name));
5347
- for (const entry of entries) {
5348
- const functionSpec = createCodexDynamicToolFunctionSpec({ entry });
5349
- if (entry.name === "openclaw" && params.directToolNames.has(entry.name)) {
5350
- specs.push(functionSpec);
5351
- continue;
5352
- }
5353
- if (entry.tool.catalogMode === "direct-only") {
5354
- directOnlyNamespaceTools.push(functionSpec);
5355
- continue;
5356
- }
5357
- if (params.loading === "direct" || params.directToolNames.has(entry.name)) {
5358
- specs.push(functionSpec);
5359
- continue;
5360
- }
5361
- namespaceTools.push({
5362
- ...functionSpec,
5363
- deferLoading: true
5364
- });
5365
- }
5366
- if (namespaceTools.length > 0) specs.push({
5367
- type: "namespace",
5368
- name: CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE,
5369
- description: "",
5370
- tools: namespaceTools
5371
- });
5372
- if (directOnlyNamespaceTools.length > 0) specs.push({
5373
- type: "namespace",
5374
- name: CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE,
5375
- description: "",
5376
- tools: directOnlyNamespaceTools
5377
- });
5378
- return specs;
5379
- }
5380
- function createCodexDynamicToolFunctionSpec(params) {
5381
- return {
5382
- type: "function",
5383
- name: params.entry.name,
5384
- description: params.entry.description,
5385
- inputSchema: params.entry.inputSchema
5386
- };
5387
- }
5388
- function projectCodexDynamicTools(tools) {
5389
- const projectedTools = [];
5390
- const quarantinedTools = [];
5391
- let length;
5392
- try {
5393
- length = tools.length;
5394
- } catch {
5395
- return {
5396
- tools: [],
5397
- quarantinedTools: [{
5398
- tool: "tool[0]",
5399
- violations: ["tool[0] is unreadable"]
5400
- }]
5401
- };
5402
- }
5403
- for (let toolIndex = 0; toolIndex < length; toolIndex += 1) {
5404
- let tool;
5405
- try {
5406
- tool = tools[toolIndex];
5407
- } catch {
5408
- quarantinedTools.push({
5409
- tool: `tool[${toolIndex}]`,
5410
- violations: [`tool[${toolIndex}] is unreadable`]
5411
- });
5412
- continue;
5413
- }
5414
- const descriptor = readCodexDynamicToolDescriptor(tool, toolIndex);
5415
- if (!descriptor.ok) {
5416
- quarantinedTools.push(descriptor.diagnostic);
5417
- continue;
5418
- }
5419
- const normalizedParameters = normalizeOpenAIToolSchemas({
5420
- provider: "openai",
5421
- modelApi: "openai-chatgpt-responses",
5422
- tools: [{ parameters: descriptor.parameters }]
5423
- })[0]?.parameters;
5424
- const projection = projectRuntimeToolInputSchema(normalizedParameters ?? descriptor.parameters, `${descriptor.name}.inputSchema`);
5425
- if (projection.violations.length > 0) {
5426
- quarantinedTools.push({
5427
- tool: descriptor.name,
5428
- violations: projection.violations
5429
- });
5430
- continue;
5431
- }
5432
- if (!isRecord(projection.schema)) {
5433
- quarantinedTools.push({
5434
- tool: descriptor.name,
5435
- violations: [`${descriptor.name}.inputSchema must be a JSON object schema`]
5436
- });
5437
- continue;
5438
- }
5439
- projectedTools.push({
5440
- tool,
5441
- name: descriptor.name,
5442
- description: descriptor.description,
5443
- inputSchema: projection.schema
5444
- });
5445
- }
5446
- return {
5447
- tools: projectedTools,
5448
- quarantinedTools
5449
- };
5450
- }
5451
- function readCodexDynamicToolDescriptor(tool, toolIndex) {
5452
- const fallbackName = `tool[${toolIndex}]`;
5453
- let name;
5454
- try {
5455
- const rawName = tool.name;
5456
- if (typeof rawName !== "string" || !rawName) return {
5457
- ok: false,
5458
- diagnostic: {
5459
- tool: fallbackName,
5460
- violations: [`${fallbackName}.name must be a non-empty string`]
5461
- }
5462
- };
5463
- const trimmedName = rawName.trim();
5464
- let nameViolation;
5465
- if (!trimmedName) nameViolation = `${rawName}.name must not be empty`;
5466
- else if (trimmedName !== rawName) nameViolation = `${rawName}.name must not have leading or trailing whitespace`;
5467
- else if (!CODEX_DYNAMIC_TOOL_NAME_PATTERN.test(rawName)) nameViolation = `${rawName}.name must match ^[a-zA-Z0-9_-]+$`;
5468
- else if (rawName.length > CODEX_DYNAMIC_TOOL_NAME_MAX_CHARS) nameViolation = `${rawName}.name must be at most ${CODEX_DYNAMIC_TOOL_NAME_MAX_CHARS} characters`;
5469
- else if (rawName === "mcp" || rawName.startsWith("mcp__")) nameViolation = `${rawName}.name is reserved by Codex app-server`;
5470
- if (nameViolation) return {
5471
- ok: false,
5472
- diagnostic: {
5473
- tool: rawName,
5474
- violations: [nameViolation]
5475
- }
5476
- };
5477
- name = rawName;
5478
- } catch {
5479
- return {
5480
- ok: false,
5481
- diagnostic: {
5482
- tool: fallbackName,
5483
- violations: [`${fallbackName}.name is unreadable`]
5484
- }
5485
- };
5486
- }
5487
- let description;
5488
- try {
5489
- description = typeof tool.description === "string" ? tool.description : "";
5490
- } catch {
5491
- return {
5492
- ok: false,
5493
- diagnostic: {
5494
- tool: name,
5495
- violations: [`${name}.description is unreadable`]
5496
- }
5497
- };
5498
- }
5499
- let parameters;
5500
- try {
5501
- parameters = tool.parameters;
5502
- } catch {
5503
- return {
5504
- ok: false,
5505
- diagnostic: {
5506
- tool: name,
5507
- violations: [`${name}.inputSchema is unreadable`]
5508
- }
5509
- };
5510
- }
5511
- return {
5512
- ok: true,
5513
- name,
5514
- description,
5515
- parameters
5516
- };
5517
- }
5518
5451
  function warnQuarantinedDynamicTools(params) {
5519
5452
  if (params.tools.length === 0) return;
5520
5453
  const unique = /* @__PURE__ */ new Map();
@@ -5569,15 +5502,26 @@ function collectToolTelemetry(params) {
5569
5502
  const response = normalizeHeartbeatToolResponse(params.result?.details);
5570
5503
  if (response) params.telemetry.heartbeatToolResponse = response;
5571
5504
  }
5572
- if (!params.isError && params.result) {
5505
+ if (params.result && !params.signal.aborted) {
5573
5506
  const media = extractToolResultMediaArtifact(params.result);
5574
5507
  if (media) {
5575
5508
  const mediaUrls = filterToolResultMediaUrls(params.toolName, media.mediaUrls, params.mediaTrustResult ?? params.result);
5576
5509
  const seen = new Set(params.telemetry.toolMediaUrls);
5577
- for (const mediaUrl of mediaUrls) if (!seen.has(mediaUrl)) {
5578
- seen.add(mediaUrl);
5579
- params.telemetry.toolMediaUrls.push(mediaUrl);
5510
+ const autoDeliveryMediaUrls = new Set(params.telemetry.toolAutoDeliveryMediaUrls);
5511
+ const rawAutoDeliveryMediaUrls = new Set(params.autoDeliveryTtsMediaUrls);
5512
+ let retainsCoreTtsMedia = false;
5513
+ for (const mediaUrl of mediaUrls) {
5514
+ if (!seen.has(mediaUrl)) {
5515
+ seen.add(mediaUrl);
5516
+ params.telemetry.toolMediaUrls.push(mediaUrl);
5517
+ }
5518
+ if (rawAutoDeliveryMediaUrls.has(mediaUrl)) {
5519
+ autoDeliveryMediaUrls.add(mediaUrl);
5520
+ retainsCoreTtsMedia = true;
5521
+ } else autoDeliveryMediaUrls.delete(mediaUrl);
5580
5522
  }
5523
+ params.telemetry.toolAutoDeliveryMediaUrls = [...autoDeliveryMediaUrls];
5524
+ if (retainsCoreTtsMedia && params.coreTtsToolResult && !params.telemetry.coreTtsToolResults.includes(params.coreTtsToolResult)) params.telemetry.coreTtsToolResults.push(params.coreTtsToolResult);
5581
5525
  if (media.audioAsVoice) params.telemetry.toolAudioAsVoice = true;
5582
5526
  }
5583
5527
  }
@@ -5814,4 +5758,4 @@ function isCronAddAction(args) {
5814
5758
  return typeof action === "string" && action.trim().toLowerCase() === "add";
5815
5759
  }
5816
5760
  //#endregion
5817
- export { shouldRequireCodexSandboxExecServerEnvironment as C, CodexNativeToolLifecycleProjector as D, readBoundedCodexRemoteWorkspaceFile as E, shouldEnableCodexAppServerNativeToolSurface as S, filterCodexVisionTools as T, resolveCodexAppServerHookChannelId as _, emitDynamicToolStartedDiagnostic as a, resolveCodexNodePlacementToolConstructionPlan as b, resolveCodexProviderWebSearchSupport as c, releaseCodexSandboxExecServerEnvironment as d, buildDynamicTools as f, resolveCodexAppServerExecutionCwd as g, formatCodexDynamicToolBuildStageSummary as h, emitDynamicToolErrorDiagnostic as i, resolveCodexProviderWebSearchSupportForClient as l, disableCodexPluginThreadConfig as m, projectCodexExecutableDynamicTools as n, emitDynamicToolTerminalDiagnostic as o, createCodexDynamicToolBuildStageTracker as p, routeCodexAppServerElicitationRequest as r, handleCodexAppServerApprovalRequest as s, createCodexDynamicToolBridge as t, ensureCodexSandboxExecServerEnvironment as u, resolveCodexExternalSandboxPolicyForOpenClawSandbox as v, shouldWarnCodexDynamicToolBuildStageSummary as w, resolveCodexSandboxEnvironmentSelection as x, resolveCodexMessageToolProvider as y };
5761
+ export { filterCodexVisionTools as C, shouldWarnCodexDynamicToolBuildStageSummary as S, CodexNativeToolLifecycleProjector as T, resolveCodexMessageToolProvider as _, emitDynamicToolStartedDiagnostic as a, shouldEnableCodexAppServerNativeToolSurface as b, ensureCodexSandboxExecServerEnvironment as c, createCodexDynamicToolBuildStageTracker as d, disableCodexPluginThreadConfig as f, resolveCodexExternalSandboxPolicyForOpenClawSandbox as g, resolveCodexAppServerHookChannelId as h, emitDynamicToolErrorDiagnostic as i, releaseCodexSandboxExecServerEnvironment as l, resolveCodexAppServerExecutionCwd as m, projectCodexExecutableDynamicTools as n, emitDynamicToolTerminalDiagnostic as o, formatCodexDynamicToolBuildStageSummary as p, routeCodexAppServerElicitationRequest as r, handleCodexAppServerApprovalRequest as s, createCodexDynamicToolBridge as t, buildDynamicTools as u, resolveCodexNodePlacementToolConstructionPlan as v, readBoundedCodexRemoteWorkspaceFile as w, shouldRequireCodexSandboxExecServerEnvironment as x, resolveCodexSandboxEnvironmentSelection as y };