@openclaw/codex 2026.9.1 → 2026.9.3

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 (109) hide show
  1. package/README.md +2 -0
  2. package/dist/api.js +18 -23
  3. package/dist/apply-B6el6hdO.js +357 -0
  4. package/dist/{attempt-client-cleanup-DYHcRQAa.js → attempt-client-cleanup-D6cR54TV.js} +113 -49
  5. package/dist/{attempt-context-DmQUNWwT.js → attempt-context-AO4DSjE0.js} +11 -9
  6. package/dist/{event-projector-usage-C5mcrGjp.js → attempt-notifications--H8zXR5-.js} +3 -89
  7. package/dist/auth-cache-key-Kq6oerr6.js +498 -0
  8. package/dist/auth-profile-CwzO4_RG.js +43 -0
  9. package/dist/auth-profile-selection-DFu9e167.js +45 -0
  10. package/dist/{binding-connection-E6uDsPlx.js → binding-connection-QqgcPCgk.js} +18 -3
  11. package/dist/{bounded-turn-wzJrwKKe.js → bounded-turn-lRpmZHFv.js} +54 -150
  12. package/dist/build-state-6RL6qohz.js +19 -0
  13. package/dist/{capabilities-dIjNVCYH.js → capabilities-BeqLPbtM.js} +2 -1
  14. package/dist/cli-metadata.js +1 -1
  15. package/dist/client-BEKu4yDx.js +3983 -0
  16. package/dist/command-dispatch-CisT9Jqq.js +21 -0
  17. package/dist/{command-formatters-CLHhEB1Q.js → command-formatters-DeTfrapr.js} +2 -2
  18. package/dist/{command-handlers-CTGb2csu.js → command-handlers-BfIsJEuA.js} +158 -76
  19. package/dist/command-rpc-BMqRQOFX.js +226 -0
  20. package/dist/{compact-_t0HQrUS.js → compact-CGJZ2rlb.js} +89 -49
  21. package/dist/config-JDfTI4VI.js +106 -0
  22. package/dist/{config-BhmfC1s6.js → config-options-jnIFvwxD.js} +208 -654
  23. package/dist/config-runtime-BWf6JLWG.js +11 -0
  24. package/dist/config-utils-C57JiJrU.js +69 -0
  25. package/dist/conversation-binding-CxOlHiix.js +889 -0
  26. package/dist/{plugin-marketplace-discovery-CfFlKqDl.js → conversation-control-CUCrSIQI.js} +36 -155
  27. package/dist/doctor-contract-api.js +8 -9
  28. package/dist/{dynamic-tools-BU9tmlYm.js → dynamic-tools-D-JBoaLt.js} +734 -698
  29. package/dist/{effective-mcp-catalog-DzYBW4h1.js → effective-mcp-catalog-J_5Ep5Um.js} +4 -3
  30. package/dist/{elicitation-input-DMkeki8d.js → elicitation-input-DPoK8F7w.js} +8 -22
  31. package/dist/ephemeral-turn-DSwzP8YY.js +101 -0
  32. package/dist/{event-projector-assistant-message-DHRFXKbS.js → event-projector-assistant-message-BmBM4qBV.js} +23 -27
  33. package/dist/event-projector-usage-CIyrYYid.js +102 -0
  34. package/dist/{harness-DXVZ5II5.js → harness-FR6eQgyO.js} +47 -17
  35. package/dist/harness.js +1 -1
  36. package/dist/history-rejection-B4nOXNXF.js +13 -0
  37. package/dist/{image-payload-sanitizer-CnfoUPCE.js → image-payload-sanitizer-tupAr-2o.js} +29 -15
  38. package/dist/incognito-session-uhrBF6wJ.js +15 -0
  39. package/dist/index.js +413 -2893
  40. package/dist/{isolated-completion-kg9kv4iH.js → isolated-completion-DHBefrzg.js} +12 -21
  41. package/dist/{managed-binary-4-Vjz5GH.js → managed-binary-DrjjRRUD.js} +17 -84
  42. package/dist/managed-thread-store-D1t5jh3G.js +66 -0
  43. package/dist/media-understanding-provider.js +4 -1
  44. package/dist/{model-catalog-CUM68Hkl.js → model-catalog-B_6M04Wo.js} +6 -5
  45. package/dist/{models-sYnEc0gd.js → models-P9k2qAof.js} +4 -4
  46. package/dist/native-execution-policy-umd0tgh4.js +128 -0
  47. package/dist/{native-subagent-monitor-CnH147jH.js → native-subagent-monitor-BbCUKRwC.js} +76 -48
  48. package/dist/node-cli-sessions-rj0ysJwM.js +527 -0
  49. package/dist/{node-exec-server.runtime-CstNHCmC.js → node-exec-server.runtime-BY_ctYnT.js} +54 -23
  50. package/dist/plan-BMJdMOEF.js +711 -0
  51. package/dist/plugin-activation-BQK-f5sV.js +133 -0
  52. package/dist/{thread-resume-C7pyVKeW.js → plugin-app-cache-key-CdSQzWyX.js} +5 -53
  53. package/dist/{plugin-approval-roundtrip-CNe-Xrjx.js → plugin-approval-roundtrip-DeW95wB5.js} +1 -1
  54. package/dist/plugin-inventory-CoRCzWW4.js +325 -0
  55. package/dist/plugin-marketplace-discovery-3akBizzF.js +134 -0
  56. package/dist/{plugin-metadata-cache-BFrPhb2c.js → plugin-metadata-cache-DgNfRAGA.js} +13 -9
  57. package/dist/protocol-CAAcVIKF.js +275 -0
  58. package/dist/{provider-capabilities-DW5p4k9w.js → provider-capabilities-W4tfK5Y5.js} +2 -2
  59. package/dist/{rate-limits-CawQJfUS.js → rate-limits-Bqnx1N4z.js} +1 -1
  60. package/dist/request-CGvGOpeR.js +198 -0
  61. package/dist/{run-attempt-D1hJO4fX.js → run-attempt-DwKds3DN.js} +1498 -1028
  62. package/dist/{runtime-artifact-C3jVimFn.js → runtime-artifact-T-NwqeSH.js} +28 -28
  63. package/dist/sandbox-guard-BcT66fWy.js +165 -0
  64. package/dist/{session-binding-BHuTCRZQ.js → session-binding-CKSepwIC.js} +118 -324
  65. package/dist/session-binding-record-CaZjYwNS.js +256 -0
  66. package/dist/session-catalog-HaDcp7Sv.js +1533 -0
  67. package/dist/session-catalog-adoption-BKLtSrwi.js +251 -0
  68. package/dist/session-catalog-archive-D0i2pGj2.js +56 -0
  69. package/dist/session-catalog-home-id-D7-aLiNt.js +190 -0
  70. package/dist/session-catalog-node-adoption-BUuDYY8R.js +182 -0
  71. package/dist/session-catalog-node-continue-CBtc4wkI.js +238 -0
  72. package/dist/session-catalog-parsing-CdD3JhdM.js +280 -0
  73. package/dist/session-catalog-provenance-DkJHo735.js +124 -0
  74. package/dist/{session-cli-CmFEXMAM.js → session-cli-CbgaHh0f.js} +3 -2
  75. package/dist/session-history-Cz-tOOYj.js +96 -0
  76. package/dist/{session-upstream-marker-C351-5Qw.js → session-history-import-DFN7sWRU.js} +5 -49
  77. package/dist/session-history-read-cxPUxyVV.js +50 -0
  78. package/dist/session-history-worker-runtime.js +60 -0
  79. package/dist/{settled-turn-context-D3SNWtUn.js → session-history.worker-CNEvGaU5.js} +75 -69
  80. package/dist/session-history.worker.js +2 -0
  81. package/dist/{session-initialization-BeWzjGiR.js → session-initialization-Jo86C60I.js} +2 -1
  82. package/dist/{session-permission-policy-DeWVTCMj.js → session-permission-policy-B22InTp_.js} +4 -7
  83. package/dist/{session-retirement-BujDjXVF.js → session-retirement-BSNG_mhf.js} +8 -7
  84. package/dist/session-upstream-marker-D15C9NHp.js +46 -0
  85. package/dist/settled-turn-context-Du6gdUDd.js +45 -0
  86. package/dist/{settled-turn-finalizer-BVJx4yoc.js → settled-turn-finalizer-BuLUrZl2.js} +54 -42
  87. package/dist/shared-client-CVhez2Uo.js +2911 -0
  88. package/dist/{side-question-BTZAII4l.js → side-question-DLbr2vUU.js} +135 -191
  89. package/dist/source-Da-h6f7e.js +518 -0
  90. package/dist/thread-archive-guard-D1bP04Tf.js +53 -0
  91. package/dist/{thread-lifecycle-DAcEJ_Om.js → thread-lifecycle-D1pzRwHB.js} +467 -977
  92. package/dist/{thread-ownership-zGokd8i1.js → thread-ownership-D6oASVRu.js} +15 -17
  93. package/dist/{thread-policy-g4KbJPzp.js → thread-policy-D8kI7yAO.js} +2 -2
  94. package/dist/thread-resume-T5cveyxA.js +52 -0
  95. package/dist/timeout-BVw8aaM1.js +33 -0
  96. package/dist/transcript-mirror-attestation-DibE0krH.js +89 -0
  97. package/dist/{transcript-mirror-DTUxFWa_.js → transcript-mirror-t5TOccct.js} +111 -379
  98. package/dist/{transport-stdio-CyPldczm.js → transport-stdio-BGjHoYLr.js} +80 -48
  99. package/dist/{upstream-session-fork-CYlBIGVt.js → upstream-session-fork-De-sLJ2r.js} +94 -166
  100. package/dist/{usage-Bj-rrzkR.js → usage-CPPTmBvZ.js} +4 -3
  101. package/dist/version-omm6IPI3.js +12 -0
  102. package/dist/{web-search-provider.runtime-D2FWSK4w.js → web-search-provider.runtime-CSVgQL0w.js} +3 -3
  103. package/openclaw.plugin.json +22 -1
  104. package/package.json +6 -6
  105. package/dist/request-DYAI5FQE.js +0 -475
  106. package/dist/session-catalog-yyvTWFOw.js +0 -3120
  107. package/dist/session-history-C5J1AKzG.js +0 -87
  108. package/dist/shared-client-DxcfF9KX.js +0 -7494
  109. package/dist/{auth-binding-Ds_QVm-X.js → auth-binding-Co2Vfcf_.js} +1 -1
@@ -1,263 +1,45 @@
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
- import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-CnfoUPCE.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";
1
+ import { a as flattenCodexDynamicToolFunctions, o as isJsonObject, r as CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE, x as readCodexPluginConfig, y as isCodexRemoteExecPlacementSandbox } from "./protocol-CAAcVIKF.js";
2
+ import { B as withDynamicToolTranscriptDetails, E as createCodexElicitationResponse, L as resolveCodexToolAbortTerminalReason, R as createFailedDynamicToolResponse, w as readCodexTurn, z as withDynamicToolExecutionState } from "./client-BEKu4yDx.js";
3
+ import { a as formatCodexDisplayText } from "./command-formatters-DeTfrapr.js";
4
+ import "./config-JDfTI4VI.js";
5
+ import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-tupAr-2o.js";
6
+ import { E as itemStatus, L as readItem, T as itemName, b as auditNativeToolUnfinishedStatus, h as isCodexNotificationForTurn, k as shouldClearTerminalPresentationForNativeItem, v as auditNativeToolName, y as auditNativeToolTerminalStatus } from "./attempt-client-cleanup-D6cR54TV.js";
7
+ import { B as emitCodexNativePreToolUseFailureDiagnostic, S as resolveCodexWebSearchPlan, it as isForcedPrivateQaCodexRuntime, nt as filterCodexDynamicTools, ot as isSystemAgentOnlyCodexDynamicToolAllowlist, rt as filterCodexDynamicToolsForDisabledNativeSurface, st as normalizeCodexDynamicToolName } from "./thread-lifecycle-D1pzRwHB.js";
8
+ import { n as resolveCodexNativeExecutionPolicy, r as resolveCodexNodeExecToolOverrides } from "./native-execution-policy-umd0tgh4.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-W4tfK5Y5.js";
10
+ 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-DeW95wB5.js";
11
+ import { createRequire } from "node:module";
10
12
  import { asNonArrayRecord, asOptionalRecord, isRecord, normalizeOptionalString, normalizeTrimmedStringList, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
11
- import { validateJsonSchemaValue } from "openclaw/plugin-sdk/json-schema-runtime";
12
- import path, { posix } from "node:path";
13
- import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
14
- import { asDateTimestampMs } from "openclaw/plugin-sdk/number-runtime";
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";
17
13
  import { createHash, randomUUID } from "node:crypto";
18
- import { resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
19
- import { formatErrorMessage as formatErrorMessage$1, redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
14
+ import { asDateTimestampMs } from "openclaw/plugin-sdk/number-runtime";
20
15
  import { killProcessTree } from "openclaw/plugin-sdk/process-runtime";
21
- import { SsrFBlockedError, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
22
- import { spawn } from "node:child_process";
16
+ import { validateJsonSchemaValue } from "openclaw/plugin-sdk/json-schema-runtime";
17
+ import path, { posix } from "node:path";
18
+ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
23
19
  import { once } from "node:events";
20
+ import { spawn } from "node:child_process";
24
21
  import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
25
22
  import { DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS, estimateToolResultTextChars, resolveLiveToolResultMaxChars, sliceToolResultTextToBudget, sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
23
+ import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
24
+ import { createStageTimingTracker, formatStageTimings } from "openclaw/plugin-sdk/time-runtime";
25
+ import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
26
+ import { fileURLToPath, pathToFileURL } from "node:url";
27
+ import { resolveAgentDir } from "openclaw/plugin-sdk/agent-runtime";
28
+ import { HEARTBEAT_RESPONSE_TOOL_NAME, buildAgentHookContextChannelFields, buildEmbeddedAttemptToolRunContext, consumeAdjustedParamsForToolCall, consumePreExecutionBlockedToolCall, createAgentToolResultMiddlewareRunner, createCodexAppServerToolResultExtensionRunner, embeddedAgentLog, extractMessagingToolSend, extractMessagingToolSendResult, extractToolResultMediaArtifact, filterProviderNormalizableTools, filterToolResultMediaUrls, finalizeAgentToolAvailability, 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";
29
+ import { formatErrorMessage as formatErrorMessage$2, redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
26
30
  import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
27
31
  import { isIP } from "node:net";
28
- import { WebSocketServer } from "ws";
29
- import { fileURLToPath, pathToFileURL } from "node:url";
30
- import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
31
32
  import { redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
32
33
  import { formatMcpCodexApprovalRemedy, requiresMcpCodexToolApproval, resolveCodexScheduledToolProjectionFactory, resolveProjectedMcpCodexToolApprovalMode, runWithCronCreatorAuthorityCapabilityResolver } from "openclaw/plugin-sdk/codex-mcp-projection";
33
34
  import { buildRemoteCommand, isToolAllowed, sanitizeEnvVars } from "openclaw/plugin-sdk/sandbox";
34
35
  import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
35
36
  import { getMediaDir } from "openclaw/plugin-sdk/media-runtime";
36
- import { consumeTrustedToolNoStartError, getCoreTtsToolResultMediaUrls } from "openclaw/plugin-sdk/agent-harness-tool-runtime";
37
+ import { consumeTrustedToolNoStartError, copyInternalToolResultState, getCoreTtsToolResultMediaUrls } from "openclaw/plugin-sdk/agent-harness-tool-runtime";
38
+ import { SsrFBlockedError, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
37
39
  import { normalizeOpenAIStrictCompatSchema } from "openclaw/plugin-sdk/provider-tools";
38
- //#region extensions/codex/src/app-server/event-projector-native-tool-lifecycle.ts
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. */
43
- var CodexNativeToolLifecycleProjector = class {
44
- constructor(context, threadId, turnId, options = {}) {
45
- this.context = context;
46
- this.threadId = threadId;
47
- this.turnId = turnId;
48
- this.options = options;
49
- this.startedAtByItem = /* @__PURE__ */ new Map();
50
- this.activeItems = /* @__PURE__ */ new Map();
51
- this.webSearchCompletionByItem = /* @__PURE__ */ new Map();
52
- this.completedItemIds = /* @__PURE__ */ new Set();
53
- this.approvalFailureDispositionByItem = /* @__PURE__ */ new Map();
54
- this.preToolUseFailureByItem = /* @__PURE__ */ new Map();
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;
80
- }
81
- handleNotification(notification) {
82
- const params = isJsonObject(notification.params) ? notification.params : void 0;
83
- if (!params || !isCodexNotificationForTurn(params, this.threadId, this.turnId)) return;
84
- if (this.pendingMcpNotifications > 0 && isMcpToolCallItemNotification(notification.method, params)) this.pendingMcpNotifications -= 1;
85
- if (notification.method === "turn/completed") {
86
- const turn = readCodexTurn(params.turn);
87
- if (!turn || turn.id !== this.turnId) return;
88
- this.turnCompleted = true;
89
- for (const item of turn.items ?? []) this.recordSnapshotItem(item);
90
- return;
91
- }
92
- if (notification.method === "rawResponseItem/completed") {
93
- const item = isJsonObject(params.item) ? params.item : void 0;
94
- if (item) this.recordRawWebSearchResult(item);
95
- return;
96
- }
97
- if (notification.method !== "item/started" && notification.method !== "item/completed") return;
98
- const item = readItem(params.item);
99
- if (!item) return;
100
- this.recordItem({
101
- phase: notification.method === "item/started" ? "start" : "result",
102
- item,
103
- sourceTimestampMs: asDateTimestampMs(notification.method === "item/started" ? params.startedAtMs : params.completedAtMs)
104
- });
105
- }
106
- recordItem(params) {
107
- const toolName = auditNativeToolName(params.item) ?? (params.item.type === "mcpToolCall" ? params.item.type : void 0);
108
- if (!toolName || this.completedItemIds.has(params.item.id)) return;
109
- if (params.phase === "start") {
110
- this.recordStarted(params.item.id, toolName, auditNativeToolUnfinishedStatus(params.item), params.sourceTimestampMs, params.item.type === "mcpToolCall" ? params.item : void 0);
111
- return;
112
- }
113
- if (params.item.type === "webSearch") {
114
- this.webSearchCompletionByItem.set(params.item.id, {
115
- runWasAborted: this.options.runAbortSignal?.aborted === true,
116
- sourceTimestampMs: params.sourceTimestampMs
117
- });
118
- return;
119
- }
120
- const itemDurationMs = typeof params.item.durationMs === "number" ? params.item.durationMs : void 0;
121
- this.recordTerminal(params.item.id, toolName, auditNativeToolTerminalStatus(params.item), {
122
- itemDurationMs,
123
- sourceTimestampMs: params.sourceTimestampMs
124
- });
125
- }
126
- recordApprovalFailureDisposition(toolCallId, disposition) {
127
- if (!this.completedItemIds.has(toolCallId)) this.approvalFailureDispositionByItem.set(toolCallId, disposition);
128
- }
129
- recordPreToolUseFailure(failure, runWasAborted = this.options.runAbortSignal?.aborted === true) {
130
- if (this.completedItemIds.has(failure.toolCallId)) return;
131
- const record = {
132
- failure,
133
- terminalReason: runWasAborted && this.options.runAbortSignal ? resolveCodexToolAbortTerminalReason(this.options.runAbortSignal) : failure.disposition
134
- };
135
- if (this.finalized) {
136
- this.completedItemIds.add(failure.toolCallId);
137
- this.emitPreToolUseFailure(record, failure.toolName, failure.durationMs);
138
- return;
139
- }
140
- this.preToolUseFailureByItem.set(failure.toolCallId, record);
141
- }
142
- recordRawWebSearchResult(item) {
143
- if (readStringField(item, "type") !== "web_search_call") return;
144
- const toolCallId = readStringField(item, "id");
145
- if (!toolCallId || this.completedItemIds.has(toolCallId)) return;
146
- const toolName = "web_search";
147
- this.recordStarted(toolCallId, toolName, "unknown");
148
- const rawStatus = readStringField(item, "status");
149
- if (rawStatus === "in_progress" || rawStatus === "running") return;
150
- const status = rawStatus === "completed" ? "completed" : rawStatus === "cancelled" ? "cancelled" : rawStatus === "failed" || rawStatus === "error" || rawStatus === "incomplete" ? "failed" : "unknown";
151
- this.recordTerminal(toolCallId, toolName, status, { sourceTimestampMs: this.webSearchCompletionByItem.get(toolCallId)?.sourceTimestampMs });
152
- }
153
- recordTerminal(toolCallId, toolName, status, options = {}) {
154
- const runWasAborted = options.runWasAborted ?? this.options.runAbortSignal?.aborted === true;
155
- const preToolUseFailure = this.preToolUseFailureByItem.get(toolCallId);
156
- this.preToolUseFailureByItem.delete(toolCallId);
157
- const approvalFailureDisposition = this.approvalFailureDispositionByItem.get(toolCallId);
158
- this.approvalFailureDispositionByItem.delete(toolCallId);
159
- this.completedItemIds.add(toolCallId);
160
- this.activeItems.delete(toolCallId);
161
- this.webSearchCompletionByItem.delete(toolCallId);
162
- const startedAt = this.startedAtByItem.get(toolCallId);
163
- this.startedAtByItem.delete(toolCallId);
164
- const endedAt = options.sourceTimestampMs ?? Date.now();
165
- const durationMs = options.itemDurationMs ?? (startedAt === void 0 ? 0 : Math.max(0, endedAt - startedAt));
166
- if (preToolUseFailure) {
167
- this.emitPreToolUseFailure(preToolUseFailure, toolName, durationMs, options.sourceTimestampMs);
168
- return;
169
- }
170
- const terminalEvent = approvalFailureDisposition ? {
171
- type: "tool.execution.error",
172
- durationMs,
173
- errorCategory: "codex_native_tool_approval",
174
- terminalReason: approvalFailureDisposition
175
- } : status === "blocked" ? {
176
- type: "tool.execution.blocked",
177
- reason: "codex_native_tool_blocked",
178
- deniedReason: "codex_native_tool_blocked"
179
- } : status === "failed" || status === "cancelled" || status === "unknown" ? {
180
- type: "tool.execution.error",
181
- durationMs,
182
- errorCategory: status === "unknown" ? "codex_native_tool_outcome_unknown" : status === "cancelled" ? "aborted" : "codex_native_tool_error",
183
- ...status === "unknown" ? { errorCode: "tool_outcome_unknown" } : {},
184
- terminalReason: status === "unknown" ? "failed" : runWasAborted && this.options.runAbortSignal ? resolveCodexToolAbortTerminalReason(this.options.runAbortSignal) : status === "cancelled" ? "cancelled" : "failed"
185
- } : {
186
- type: "tool.execution.completed",
187
- durationMs
188
- };
189
- emitTrustedDiagnosticEvent({
190
- ...this.buildBase(toolCallId, toolName),
191
- ...terminalEvent,
192
- ...options.sourceTimestampMs !== void 0 ? { sourceTimestampMs: options.sourceTimestampMs } : {}
193
- });
194
- }
195
- finalizeActive(runWasAborted = this.options.runAbortSignal?.aborted === true) {
196
- this.finalized = true;
197
- for (const [toolCallId, { toolName, unfinishedStatus }] of this.activeItems) {
198
- const webSearchCompletion = this.webSearchCompletionByItem.get(toolCallId);
199
- const itemRunWasAborted = webSearchCompletion ? webSearchCompletion.runWasAborted : runWasAborted;
200
- this.recordTerminal(toolCallId, toolName, unfinishedStatus, {
201
- runWasAborted: itemRunWasAborted,
202
- sourceTimestampMs: webSearchCompletion?.sourceTimestampMs
203
- });
204
- }
205
- for (const [toolCallId, record] of this.preToolUseFailureByItem) if (!this.completedItemIds.has(toolCallId)) this.recordTerminal(toolCallId, record.failure.toolName, "failed", { itemDurationMs: record.failure.durationMs });
206
- this.activeItems.clear();
207
- this.webSearchCompletionByItem.clear();
208
- this.approvalFailureDispositionByItem.clear();
209
- this.preToolUseFailureByItem.clear();
210
- }
211
- emitPreToolUseFailure(record, toolName, durationMs, sourceTimestampMs) {
212
- emitCodexNativePreToolUseFailureDiagnostic({
213
- agentId: this.context.agentId,
214
- sessionId: this.context.sessionId,
215
- sessionKey: this.context.sessionKey,
216
- runId: this.context.runId,
217
- failure: {
218
- ...record.failure,
219
- toolName,
220
- durationMs
221
- },
222
- terminalReason: record.terminalReason,
223
- sourceTimestampMs
224
- });
225
- }
226
- recordSnapshotItem(item) {
227
- if (!auditNativeToolName(item) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
228
- const toolName = auditNativeToolName(item);
229
- if (!toolName) return;
230
- this.recordStarted(item.id, toolName, auditNativeToolUnfinishedStatus(item));
231
- this.recordItem({
232
- phase: "result",
233
- item
234
- });
235
- }
236
- recordStarted(toolCallId, toolName, unfinishedStatus, sourceTimestampMs, mcpToolCall) {
237
- if (this.activeItems.has(toolCallId)) return;
238
- this.startedAtByItem.set(toolCallId, sourceTimestampMs ?? Date.now());
239
- this.activeItems.set(toolCallId, {
240
- toolName,
241
- unfinishedStatus,
242
- mcpToolCall
243
- });
244
- emitTrustedDiagnosticEvent({
245
- type: "tool.execution.started",
246
- ...this.buildBase(toolCallId, toolName),
247
- ...sourceTimestampMs !== void 0 ? { sourceTimestampMs } : {}
248
- });
249
- }
250
- buildBase(toolCallId, toolName) {
251
- return {
252
- agentId: this.context.agentId,
253
- runId: this.context.runId,
254
- sessionId: this.context.sessionId,
255
- sessionKey: this.context.sessionKey,
256
- toolName,
257
- toolCallId
258
- };
259
- }
260
- };
40
+ //#region extensions/codex/src/app-server/dynamic-tool-build-state.ts
41
+ /** Mutable dependency seam shared by dynamic-tool construction and its behavioral tests. */
42
+ const dynamicToolBuildState = {};
261
43
  //#endregion
262
44
  //#region extensions/codex/src/app-server/remote-workspace-path.ts
263
45
  /** Projects a gateway workspace path into the remote Codex execution workspace. */
@@ -307,273 +89,6 @@ function joinRemoteWorkspacePath(remoteRoot, suffix) {
307
89
  return remoteRoot.endsWith("/") ? `${remoteRoot}${suffix}` : `${remoteRoot}/${suffix}`;
308
90
  }
309
91
  //#endregion
310
- //#region extensions/codex/src/app-server/remote-workspace-media.ts
311
- const REMOTE_WORKSPACE_MEDIA_TIMEOUT_MS = 6e4;
312
- const REMOTE_WORKSPACE_MEDIA_MAX_BYTES = 67108864;
313
- const REMOTE_WORKSPACE_MEDIA_MAX_ATTACHMENTS = 16;
314
- const CODEX_REMOTE_MEDIA_CHUNK_BYTES = 524288;
315
- const CODEX_REMOTE_COMMAND_DEFAULT_OUTPUT_BYTES = 1048576;
316
- const CODEX_BOUNDED_REMOTE_FILE_READER = [
317
- "try{",
318
- "const fs=require(\"node:fs\");",
319
- "const path=require(\"node:path\");",
320
- "const file=process.argv[1];",
321
- "const max=Number(process.argv[2]);",
322
- "const offset=Number(process.argv[3]);",
323
- "const chunk=Number(process.argv[4]);",
324
- "const workspace=process.argv[5];",
325
- "if(!Number.isSafeInteger(max)||max<0)throw Error(\"invalid media byte limit\");",
326
- "if(!Number.isSafeInteger(offset)||offset<0||!Number.isSafeInteger(chunk)||chunk<=0)throw Error(\"invalid media chunk\");",
327
- "if(fs.lstatSync(file).isSymbolicLink())throw Error(\"symbolic links are not allowed\");",
328
- "const noFollow=fs.constants.O_NOFOLLOW??0;",
329
- "const fd=fs.openSync(file,fs.constants.O_RDONLY|noFollow);",
330
- "try{",
331
- "const before=fs.fstatSync(fd);",
332
- "if(!before.isFile())throw Error(\"not a regular file\");",
333
- "if(workspace){",
334
- "const descriptor=process.platform===\"linux\"?fs.realpathSync(`/proc/self/fd/${fd}`):fs.realpathSync(file);",
335
- "const relative=path.relative(fs.realpathSync(workspace),descriptor);",
336
- "if(!relative||relative===\"..\"||relative.startsWith(`..${path.sep}`)||path.isAbsolute(relative))throw Error(\"file escapes remote workspace\");",
337
- "const verified=fs.statSync(descriptor);",
338
- "if(verified.dev!==before.dev||verified.ino!==before.ino)throw Error(\"file changed while being opened\");",
339
- "}",
340
- "if(before.size>max)throw Error(`file exceeds limit of ${max} bytes`);",
341
- "if(offset>before.size)throw Error(\"file changed while being read\");",
342
- "const expected=Math.min(chunk,before.size-offset);",
343
- "const buffer=Buffer.allocUnsafe(expected);",
344
- "let total=0;",
345
- "while(total<buffer.length){",
346
- "const count=fs.readSync(fd,buffer,total,buffer.length-total,offset+total);",
347
- "if(count===0)break;total+=count;",
348
- "}",
349
- "if(total!==expected)throw Error(\"file changed while being read\");",
350
- "const after=fs.fstatSync(fd);",
351
- "const revision=stat=>[stat.dev,stat.ino,stat.size,stat.mtimeMs,stat.ctimeMs].join(\":\");",
352
- "if(!after.isFile()||revision(after)!==revision(before))throw Error(\"file changed while being read\");",
353
- "process.stdout.write(JSON.stringify({dataBase64:buffer.toString(\"base64\"),size:before.size,revision:revision(before)}));",
354
- "}finally{fs.closeSync(fd)}",
355
- "}catch(error){process.stderr.write(error instanceof Error?error.message:String(error));process.exitCode=1}"
356
- ].join("");
357
- const MESSAGE_MEDIA_KEYS = [
358
- "media",
359
- "mediaUrl",
360
- "media_url",
361
- "path",
362
- "filePath",
363
- "fileUrl",
364
- "imageUrl",
365
- "image_url"
366
- ];
367
- const MESSAGE_MEDIA_ARRAY_KEYS = [
368
- "mediaUrls",
369
- "media_urls",
370
- "imageUrls",
371
- "image_urls"
372
- ];
373
- const ATTACHMENT_MEDIA_KEYS = [
374
- "media",
375
- "mediaUrl",
376
- "path",
377
- "filePath",
378
- "fileUrl",
379
- "url"
380
- ];
381
- /** Reads actual remote bytes with a cap enforced by Codex before transport. */
382
- async function readBoundedCodexRemoteWorkspaceFile(params) {
383
- if (!Number.isSafeInteger(params.maxBytes) || params.maxBytes < 0) throw new Error("Codex remote workspace upload requires a valid media byte limit.");
384
- params.signal?.throwIfAborted();
385
- const chunks = [];
386
- let offset = 0;
387
- let expectedSize;
388
- let expectedRevision;
389
- const startedAt = Date.now();
390
- do {
391
- params.signal?.throwIfAborted();
392
- const timeoutMs = params.timeoutMs === void 0 ? void 0 : params.timeoutMs - (Date.now() - startedAt);
393
- if (timeoutMs !== void 0 && timeoutMs <= 0) throw new Error("Codex remote workspace file transfer timed out.");
394
- let response;
395
- try {
396
- response = await params.client.request("command/exec", {
397
- command: [
398
- "node",
399
- "-e",
400
- CODEX_BOUNDED_REMOTE_FILE_READER,
401
- "--",
402
- params.path,
403
- String(params.maxBytes),
404
- String(offset),
405
- String(CODEX_REMOTE_MEDIA_CHUNK_BYTES),
406
- ...params.workspaceRoot ? [params.workspaceRoot] : []
407
- ],
408
- env: {
409
- NODE_OPTIONS: null,
410
- NODE_PATH: null
411
- },
412
- ...timeoutMs === void 0 ? {} : { timeoutMs }
413
- }, {
414
- signal: params.signal,
415
- timeoutMs
416
- });
417
- } catch (error) {
418
- if (error instanceof Error && /failed to spawn|executable.*not found|\bENOENT\b/iu.test(error.message)) throw new Error("Codex remote workspace file transfer requires Node.js on the remote app-server host.", { cause: error });
419
- throw error;
420
- }
421
- if (!response || response.exitCode !== 0) {
422
- const detail = typeof response?.stderr === "string" ? response.stderr.trim() : "";
423
- throw new Error(`Codex remote workspace artifact could not be read: ${params.path}${detail ? `: ${detail}` : ""}`);
424
- }
425
- if (typeof response.stdout !== "string" || response.stdout.length > CODEX_REMOTE_COMMAND_DEFAULT_OUTPUT_BYTES) throw new Error("Codex remote workspace artifact exceeded the native command output cap.");
426
- let payload;
427
- try {
428
- payload = JSON.parse(response.stdout);
429
- } catch {
430
- throw new Error("Codex remote workspace artifact returned invalid chunk data.");
431
- }
432
- if (!payload || typeof payload !== "object" || Array.isArray(payload)) throw new Error("Codex remote workspace artifact returned invalid chunk data.");
433
- const chunk = payload;
434
- if (typeof chunk.dataBase64 !== "string" || !Number.isSafeInteger(chunk.size) || chunk.size < 0 || chunk.size > params.maxBytes || typeof chunk.revision !== "string" || !chunk.revision) throw new Error("Codex remote workspace artifact returned invalid or oversized chunk data.");
435
- if (expectedSize === void 0) {
436
- expectedSize = chunk.size;
437
- expectedRevision = chunk.revision;
438
- }
439
- if (chunk.size !== expectedSize || chunk.revision !== expectedRevision) throw new Error("Codex remote workspace artifact changed during chunked transfer.");
440
- const remainingBytes = expectedSize - offset;
441
- const expectedChunkBytes = Math.min(CODEX_REMOTE_MEDIA_CHUNK_BYTES, remainingBytes);
442
- if (chunk.dataBase64.length > Math.ceil(expectedChunkBytes / 3) * 4) throw new Error("Codex remote workspace artifact returned oversized chunk data.");
443
- const buffer = Buffer.from(chunk.dataBase64, "base64");
444
- if (buffer.byteLength !== expectedChunkBytes || buffer.toString("base64") !== chunk.dataBase64) throw new Error("Codex remote workspace artifact returned invalid chunk data.");
445
- chunks.push(buffer);
446
- offset += buffer.byteLength;
447
- } while (offset < (expectedSize ?? 0));
448
- return { dataBase64: Buffer.concat(chunks, offset).toString("base64") };
449
- }
450
- /** Stages authoritative bounded remote bytes into immutable Gateway-owned media. */
451
- async function prepareCodexRemoteWorkspaceMessageMedia(params) {
452
- const { localWorkspaceRoot, remoteWorkspaceRoot } = params;
453
- if (!localWorkspaceRoot || !remoteWorkspaceRoot) return params.args;
454
- const remotePathsByLocalPath = /* @__PURE__ */ new Map();
455
- const gatewayManagedPaths = /* @__PURE__ */ new Set();
456
- const gatewayMediaRoot = getMediaDir();
457
- let attachmentEntries = 0;
458
- const mapMediaPath = (value) => {
459
- if (typeof value !== "string") return value;
460
- if (path.isAbsolute(value) && isPathStrictlyInside(gatewayMediaRoot, value)) {
461
- attachmentEntries += 1;
462
- gatewayManagedPaths.add(value);
463
- return value;
464
- }
465
- const mapped = mapCodexAppServerLocalWorkspacePath({
466
- value,
467
- localWorkspaceRoot,
468
- remoteWorkspaceRoot
469
- });
470
- if (value.trim() && !isCodexPassThroughMediaSource(value)) {
471
- attachmentEntries += 1;
472
- remotePathsByLocalPath.set(mapped, mapCodexAppServerRemoteWorkspacePath({
473
- value: mapped,
474
- localWorkspaceRoot,
475
- remoteWorkspaceRoot
476
- }));
477
- }
478
- return mapped;
479
- };
480
- let mappedArgs = params.args;
481
- const setMappedValue = (key, value) => {
482
- if (value === params.args[key]) return;
483
- if (mappedArgs === params.args) mappedArgs = { ...params.args };
484
- mappedArgs[key] = value;
485
- };
486
- for (const key of MESSAGE_MEDIA_KEYS) setMappedValue(key, mapMediaPath(params.args[key]));
487
- for (const key of MESSAGE_MEDIA_ARRAY_KEYS) {
488
- const value = params.args[key];
489
- if (Array.isArray(value)) {
490
- const mapped = value.map(mapMediaPath);
491
- if (mapped.some((entry, index) => entry !== value[index])) setMappedValue(key, mapped);
492
- }
493
- }
494
- if (Array.isArray(params.args.attachments)) {
495
- const attachments = params.args.attachments;
496
- const mapped = attachments.map((attachment) => {
497
- if (!attachment || typeof attachment !== "object" || Array.isArray(attachment)) return attachment;
498
- const record = attachment;
499
- let mappedAttachment = record;
500
- for (const key of ATTACHMENT_MEDIA_KEYS) {
501
- const value = mapMediaPath(record[key]);
502
- if (value !== record[key]) {
503
- if (mappedAttachment === record) mappedAttachment = { ...record };
504
- mappedAttachment[key] = value;
505
- }
506
- }
507
- return mappedAttachment;
508
- });
509
- if (mapped.some((attachment, index) => attachment !== attachments[index])) setMappedValue("attachments", mapped);
510
- }
511
- if (attachmentEntries > REMOTE_WORKSPACE_MEDIA_MAX_ATTACHMENTS) throw new Error(`Codex remote workspace upload exceeds the ${REMOTE_WORKSPACE_MEDIA_MAX_ATTACHMENTS}-attachment limit.`);
512
- for (const managedPath of gatewayManagedPaths) await assertGatewayManagedMediaPath(managedPath, gatewayMediaRoot);
513
- if (remotePathsByLocalPath.size === 0) return mappedArgs;
514
- const readRemoteFile = params.readRemoteFile;
515
- if (!readRemoteFile) throw new Error("Codex remote workspace file transfer requires an active app-server client.");
516
- const maxBytes = params.maxBytes ?? REMOTE_WORKSPACE_MEDIA_MAX_BYTES;
517
- const timeoutMs = params.timeoutMs ?? REMOTE_WORKSPACE_MEDIA_TIMEOUT_MS;
518
- const deadline = Date.now() + timeoutMs;
519
- const stagedPaths = /* @__PURE__ */ new Map();
520
- let totalBytes = 0;
521
- for (const [localPath, remotePath] of remotePathsByLocalPath) {
522
- params.signal?.throwIfAborted();
523
- const remainingBytes = maxBytes - totalBytes;
524
- const remainingMs = deadline - Date.now();
525
- if (remainingMs <= 0) throw new Error("Codex remote workspace attachment batch timed out.");
526
- const response = await readRemoteFile({
527
- path: remotePath,
528
- maxBytes: remainingBytes,
529
- workspaceRoot: remoteWorkspaceRoot,
530
- signal: params.signal,
531
- timeoutMs: remainingMs
532
- });
533
- if (!response || typeof response.dataBase64 !== "string") throw new Error(`Codex remote workspace artifact returned no file data: ${remotePath}`);
534
- if (response.dataBase64.length > Math.ceil(remainingBytes / 3) * 4) throw new Error(`Codex remote workspace artifact exceeds the limit of ${remainingBytes} bytes.`);
535
- const remoteBuffer = Buffer.from(response.dataBase64, "base64");
536
- if (remoteBuffer.byteLength > remainingBytes || remoteBuffer.toString("base64") !== response.dataBase64) throw new Error(`Codex remote workspace artifact returned invalid or oversized file data: ${remotePath}`);
537
- totalBytes += remoteBuffer.byteLength;
538
- const saved = await saveMediaBuffer(remoteBuffer, void 0, "outbound", maxBytes, path.basename(remotePath));
539
- stagedPaths.set(localPath, saved.path);
540
- }
541
- return mapMessageMediaValues(mappedArgs, (value) => stagedPaths.get(value) ?? value);
542
- }
543
- async function assertGatewayManagedMediaPath(value, mediaRoot) {
544
- const opened = await (await root(mediaRoot, { symlinks: "reject" })).open(path.relative(mediaRoot, value), { symlinks: "reject" });
545
- try {
546
- if (!(await opened.handle.stat()).isFile()) throw new Error(`Codex Gateway-managed media is not a regular file: ${value}`);
547
- } finally {
548
- await opened[Symbol.asyncDispose]();
549
- }
550
- }
551
- function mapMessageMediaValues(args, mapValue) {
552
- const mapped = { ...args };
553
- for (const key of MESSAGE_MEDIA_KEYS) {
554
- const value = mapped[key];
555
- if (typeof value === "string") mapped[key] = mapValue(value);
556
- }
557
- for (const key of MESSAGE_MEDIA_ARRAY_KEYS) {
558
- const value = mapped[key];
559
- if (Array.isArray(value)) mapped[key] = value.map((entry) => typeof entry === "string" ? mapValue(entry) : entry);
560
- }
561
- if (Array.isArray(mapped.attachments)) mapped.attachments = mapped.attachments.map((attachment) => {
562
- if (!attachment || typeof attachment !== "object" || Array.isArray(attachment)) return attachment;
563
- const record = { ...attachment };
564
- for (const key of ATTACHMENT_MEDIA_KEYS) {
565
- const value = record[key];
566
- if (typeof value === "string") record[key] = mapValue(value);
567
- }
568
- return record;
569
- });
570
- return mapped;
571
- }
572
- //#endregion
573
- //#region extensions/codex/src/app-server/dynamic-tool-build-state.ts
574
- /** Mutable dependency seam shared by dynamic-tool construction and its behavioral tests. */
575
- const dynamicToolBuildState = {};
576
- //#endregion
577
92
  //#region extensions/codex/src/app-server/vision-tools.ts
578
93
  /**
579
94
  * Codex's enabled native surface includes its stable view_image loader. Keep
@@ -641,46 +156,23 @@ function resolveCodexAppServerHookChannelId(params, sandboxSessionKey) {
641
156
  }
642
157
  const CODEX_DYNAMIC_TOOL_BUILD_WARN_TOTAL_MS = 1e3;
643
158
  const CODEX_DYNAMIC_TOOL_BUILD_WARN_STAGE_MS = 500;
644
- /** Creates cheap optional timing instrumentation for the dynamic-tool hot path. */
645
- function createCodexDynamicToolBuildStageTracker(options = {}) {
646
- if (!options.enabled) return {
647
- mark() {},
648
- snapshot() {
649
- return {
650
- totalMs: 0,
651
- stages: []
652
- };
653
- }
654
- };
655
- const startedAt = Date.now();
656
- let previousAt = startedAt;
657
- const stages = [];
658
- const toMs = (value) => Math.max(0, Math.round(value));
159
+ /** Captures bounded preparation stages before a slow turn needs diagnosis. */
160
+ function createCodexDynamicToolBuildStageTracker() {
161
+ const { mark, snapshot } = createStageTimingTracker();
659
162
  return {
660
- mark(name) {
661
- const currentAt = Date.now();
662
- stages.push({
663
- name,
664
- durationMs: toMs(currentAt - previousAt),
665
- elapsedMs: toMs(currentAt - startedAt)
666
- });
667
- previousAt = currentAt;
668
- },
669
- snapshot() {
670
- return {
671
- totalMs: toMs(Date.now() - startedAt),
672
- stages: stages.slice()
673
- };
674
- }
163
+ mark,
164
+ snapshot
675
165
  };
676
166
  }
677
167
  /** Returns true when dynamic-tool construction is slow enough to warrant a warning log. */
678
- function shouldWarnCodexDynamicToolBuildStageSummary(summary) {
679
- return summary.totalMs >= CODEX_DYNAMIC_TOOL_BUILD_WARN_TOTAL_MS || summary.stages.some((stage) => stage.durationMs >= CODEX_DYNAMIC_TOOL_BUILD_WARN_STAGE_MS);
168
+ function shouldWarnCodexDynamicToolBuildStageSummary(summary, profilerEnabled = false) {
169
+ const totalWarnMs = profilerEnabled ? CODEX_DYNAMIC_TOOL_BUILD_WARN_TOTAL_MS : 1e4;
170
+ const stageWarnMs = profilerEnabled ? CODEX_DYNAMIC_TOOL_BUILD_WARN_STAGE_MS : 5e3;
171
+ return summary.totalMs >= totalWarnMs || summary.stages.some((stage) => stage.durationMs >= stageWarnMs);
680
172
  }
681
173
  /** Formats per-stage timings into the compact form used by Codex app-server logs. */
682
174
  function formatCodexDynamicToolBuildStageSummary(summary) {
683
- return summary.stages.length > 0 ? summary.stages.map((stage) => `${stage.name}:${stage.durationMs}ms@${stage.elapsedMs}ms`).join(",") : "none";
175
+ return formatStageTimings(summary.stages);
684
176
  }
685
177
  /** Builds, filters, and normalizes Codex-compatible runtime tools for a single turn. */
686
178
  async function buildDynamicTools(input) {
@@ -702,9 +194,9 @@ async function buildDynamicTools(input) {
702
194
  input.onWebSearchPolicyResolved?.(false);
703
195
  return [];
704
196
  }
705
- const toolBuildStages = createCodexDynamicToolBuildStageTracker({ enabled: input.profilerEnabled });
197
+ const toolBuildStages = createCodexDynamicToolBuildStageTracker();
706
198
  const modelHasVision = params.model.input?.includes("image") ?? false;
707
- const agentDir = params.agentDir ?? resolveAgentDir$1(params.config ?? {}, input.sessionAgentId);
199
+ const agentDir = params.agentDir ?? resolveAgentDir(params.config ?? {}, input.sessionAgentId);
708
200
  const injectedOpenClawCodingToolsFactory = dynamicToolBuildState.openClawCodingToolsFactory;
709
201
  const nativeExecutionPolicy = resolveCodexNativeExecutionPolicyForRun(params, {
710
202
  agentId: input.policyAgentId,
@@ -738,31 +230,17 @@ async function buildDynamicTools(input) {
738
230
  ...toolConstructionPlan ? { toolConstructionPlan } : {},
739
231
  messageProvider: resolveCodexMessageToolProvider(params),
740
232
  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,
233
+ ...params.onToolResult ? { questionPrompt: {
234
+ send: params.onToolResult,
235
+ ...params.messageChannel ? { messageChannel: params.messageChannel } : {}
236
+ } } : {},
757
237
  inputProvenance: params.inputProvenance,
758
238
  trustedInternalHandoff: params.trustedInternalHandoff,
759
- scheduledToolPolicy: params.scheduledToolPolicy,
760
239
  allowGatewaySubagentBinding: params.allowGatewaySubagentBinding || isForcedPrivateQaCodexRuntime(),
761
240
  sessionKey: input.sandboxSessionKey,
762
241
  runSessionKey: params.sessionKey && params.sessionKey !== input.sandboxSessionKey ? params.sessionKey : void 0,
763
242
  sessionId: params.sessionId,
764
243
  runId: params.runId,
765
- approvalReviewerDeviceId: params.approvalReviewerDeviceId,
766
244
  agentDir,
767
245
  preparedModelRuntime: params.preparedModelRuntime,
768
246
  cwd: input.effectiveCwd ?? input.effectiveWorkspace,
@@ -787,25 +265,18 @@ async function buildDynamicTools(input) {
787
265
  modelAuthMode: resolveModelAuthMode(params.model.provider, params.config, params.toolAuthProfileStore ?? params.authProfileStore, { workspaceDir: input.effectiveWorkspace }),
788
266
  suppressManagedWebSearch: false,
789
267
  webFetchHostnameAllowlistRef,
790
- currentChannelId: params.currentChannelId,
791
- currentMessagingTarget: params.currentMessagingTarget,
792
268
  hookChannelId: resolveCodexAppServerHookChannelId(params, input.sandboxSessionKey),
793
- currentThreadTs: params.currentThreadTs,
794
- currentMessageId: params.currentMessageId,
795
- replyToMode: params.replyToMode,
796
- hasRepliedRef: params.hasRepliedRef,
797
269
  modelHasVision,
798
270
  computerContextEpoch: input.computerContextEpoch,
271
+ oneShotCliRun: params.oneShotCliRun,
799
272
  registerRunCleanup: input.registerRunCleanup,
800
273
  requireExplicitMessageTarget: params.requireExplicitMessageTarget ?? isSubagentSessionKey(params.sessionKey),
801
- sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
802
- taskSuggestionDeliveryMode: params.taskSuggestionDeliveryMode,
803
274
  disableMessageTool: input.ignoreDisableMessageTool ? false : params.disableMessageTool,
804
275
  forceMessageTool: shouldForceMessageTool(messagePolicyParams),
805
276
  enableHeartbeatTool: params.trigger === "heartbeat" || input.forceHeartbeatTool === true,
806
277
  forceHeartbeatTool: params.trigger === "heartbeat" || input.forceHeartbeatTool === true,
807
278
  onYield: (message, acknowledgment) => {
808
- input.onYieldDetected(acknowledgment);
279
+ input.onYieldDetected(message, acknowledgment);
809
280
  input.onCodexAppServerEvent?.({
810
281
  stream: "codex_app_server.tool",
811
282
  data: {
@@ -825,6 +296,7 @@ async function buildDynamicTools(input) {
825
296
  cronCreatorToolAllowlistCaptureRef: input.cronCreatorToolAllowlistCaptureRef,
826
297
  cronCreatorAuthorityUnavailableReason: input.cronCreatorAuthorityUnavailableReason
827
298
  };
299
+ input.onMessageToolTargetResolved?.(options.requireExplicitMessageTarget === true);
828
300
  const buildOpenClawCodingTools = () => {
829
301
  const bindingOptions = { cwd: input.effectiveCwd ?? input.effectiveWorkspace };
830
302
  if (injectedOpenClawCodingToolsFactory) return params.hostCapabilities.bindToolSurface(injectedOpenClawCodingToolsFactory(options), bindingOptions);
@@ -911,7 +383,7 @@ async function buildDynamicTools(input) {
911
383
  }))
912
384
  });
913
385
  const summary = toolBuildStages.snapshot();
914
- if (shouldWarnCodexDynamicToolBuildStageSummary(summary)) {
386
+ if (shouldWarnCodexDynamicToolBuildStageSummary(summary, input.profilerEnabled)) {
915
387
  const phase = input.forceHeartbeatTool ? "registered-tools" : "runtime-tools";
916
388
  embeddedAgentLog.warn(`codex app-server dynamic tool build timings runId=${params.runId} sessionId=${params.sessionId} phase=${phase} totalMs=${summary.totalMs} stages=${formatCodexDynamicToolBuildStageSummary(summary)}`, {
917
389
  runId: params.runId,
@@ -1033,82 +505,589 @@ function disableCodexPluginThreadConfig(pluginConfig) {
1033
505
  ...config.codexPlugins,
1034
506
  enabled: false
1035
507
  }
1036
- };
508
+ };
509
+ }
510
+ /** Adds sandbox_exec/process aliases when native Code Mode cannot directly honor the sandbox. */
511
+ function addSandboxShellDynamicToolsIfAvailable(filteredTools, allTools, input, executionPolicy) {
512
+ if (isCodexMemoryFlushRun(input.params) || !executionPolicy.nativeToolSurfaceAllowed || !input.sandbox?.enabled || !input.sandbox.backendId.trim() || input.nativeToolSurfaceEnabled !== false || isSandboxShellDynamicToolExcluded(input.pluginConfig)) return filteredTools;
513
+ const execTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "exec");
514
+ const processTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "process");
515
+ if (!execTool || !processTool) return filteredTools;
516
+ const sandboxExecTool = {
517
+ ...execTool,
518
+ name: "sandbox_exec",
519
+ description: "Run a shell command through OpenClaw's configured sandbox backend for this session. Use when OpenClaw sandboxing is active or when a command must execute in the sandbox backend, such as an SSH-backed sandbox or Docker container-path bind layout. Use Codex's native shell only when no OpenClaw sandbox is active and native Code Mode is available.",
520
+ execute: async (toolCallId, args, signal, onUpdate) => {
521
+ const result = await execTool.execute(toolCallId, args, signal, onUpdate);
522
+ return {
523
+ ...result,
524
+ content: result.content.map((item) => item.type === "text" ? Object.assign({}, item, { text: item.text.replace("Use process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.", "Use sandbox_process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.") }) : item)
525
+ };
526
+ }
527
+ };
528
+ const sandboxProcessTool = {
529
+ ...processTool,
530
+ name: "sandbox_process",
531
+ description: "Manage background shell sessions through OpenClaw's configured sandbox backend for this session: list, poll, log, write, send-keys, submit, paste, kill, clear, or remove. Use only for sandbox follow-up; use Codex's native shell session handling only when no OpenClaw sandbox is active and native Code Mode is available."
532
+ };
533
+ return [
534
+ ...filteredTools,
535
+ sandboxExecTool,
536
+ sandboxProcessTool
537
+ ];
538
+ }
539
+ function isSandboxShellDynamicToolExcluded(config) {
540
+ return isCodexDynamicToolExcluded(config, [
541
+ "exec",
542
+ "sandbox_exec",
543
+ "process",
544
+ "sandbox_process"
545
+ ]);
546
+ }
547
+ async function addNodeShellDynamicToolsIfNeeded(filteredTools, allTools, input, nodePolicy) {
548
+ if (isCodexMemoryFlushRun(input.params)) return filteredTools;
549
+ const nodeExecIsDefault = nodePolicy.effectiveExecHost === "node";
550
+ const nodeExecAvailableFromAuto = nodePolicy.requestedExecHost === "auto" && nodePolicy.effectiveExecHost === "gateway";
551
+ if (!nodeExecIsDefault && !nodeExecAvailableFromAuto) return filteredTools;
552
+ const execTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "exec");
553
+ if (!execTool || isCodexDynamicToolExcluded(input.pluginConfig, ["exec", "node_exec"]) || filteredTools.some((tool) => normalizeCodexDynamicToolName(tool.name) === "node_exec")) return filteredTools;
554
+ const nodeExec = await createNodeExecAliasDynamicTool(execTool, nodePolicy.node, input.runAbortController.signal, input.nodeExecAvailability);
555
+ return nodeExec ? [...filteredTools, nodeExec] : filteredTools;
556
+ }
557
+ function shouldKeepOpenClawShellDynamicTools(input, nodePolicy) {
558
+ return !isCodexMemoryFlushRun(input.params) && input.nativeToolSurfaceEnabled === false && input.sandbox?.enabled !== true && nodePolicy.effectiveExecHost !== "node";
559
+ }
560
+ /** Keeps replacement shell tools direct even when model metadata mandates Codex Code Mode. */
561
+ function placeDisabledNativeShellToolsInDirectNamespace(tools, nativeToolSurfaceEnabled) {
562
+ if (nativeToolSurfaceEnabled !== false) return tools;
563
+ for (const tool of tools) if (CODEX_DISABLED_NATIVE_SHELL_DYNAMIC_TOOLS.has(normalizeCodexDynamicToolName(tool.name))) tool.catalogMode = "direct-only";
564
+ return tools;
565
+ }
566
+ /** Applies a normalized tool allowlist while preserving shell aliases for exec/process. */
567
+ function filterCodexDynamicToolsForAllowlist(tools, toolsAllow) {
568
+ if (!toolsAllow) return tools;
569
+ if (toolsAllow.length === 0) return [];
570
+ if (hasWildcardCodexToolsAllow(toolsAllow)) return tools;
571
+ const allowSet = new Set(toolsAllow.map((name) => normalizeCodexDynamicToolName(name)).filter(Boolean));
572
+ return tools.filter((tool) => {
573
+ const normalized = normalizeCodexDynamicToolName(tool.name);
574
+ return allowSet.has(normalized) || normalized === "sandbox_exec" && allowSet.has("exec") || normalized === "sandbox_process" && (allowSet.has("exec") || allowSet.has("process")) || normalized === "gateway_exec" && allowSet.has("exec") || normalized === "gateway_process" && (allowSet.has("exec") || allowSet.has("process")) || normalized === "node_exec" && allowSet.has("exec");
575
+ });
576
+ }
577
+ /** Detects the wildcard allowlist marker after Codex tool-name normalization. */
578
+ function hasWildcardCodexToolsAllow(toolsAllow) {
579
+ return toolsAllow.some((name) => normalizeCodexDynamicToolName(name) === "*");
580
+ }
581
+ /** Forces message delivery through the message tool when the source channel requires it. */
582
+ function shouldForceMessageTool(params) {
583
+ return params.disableMessageTool !== true && params.sourceReplyDeliveryMode === "message_tool_only";
584
+ }
585
+ //#endregion
586
+ //#region extensions/codex/src/app-server/event-projector-native-tool-lifecycle.ts
587
+ function isMcpToolCallItemNotification(method, params) {
588
+ return (method === "item/started" || method === "item/completed") && isJsonObject(params.item) && params.item.type === "mcpToolCall";
589
+ }
590
+ /** Owns native item lifetimes, outcome order, presentation, and approval correlation. */
591
+ var CodexNativeToolLifecycleProjector = class {
592
+ constructor(context, threadId, turnId, options = {}) {
593
+ this.context = context;
594
+ this.threadId = threadId;
595
+ this.turnId = turnId;
596
+ this.options = options;
597
+ this.terminalPresentationClearedItemIds = /* @__PURE__ */ new Set();
598
+ this.nativeToolOutcomeOrdinals = /* @__PURE__ */ new Map();
599
+ this.startedAtByItem = /* @__PURE__ */ new Map();
600
+ this.activeItems = /* @__PURE__ */ new Map();
601
+ this.webSearchCompletionByItem = /* @__PURE__ */ new Map();
602
+ this.completedItemIds = /* @__PURE__ */ new Set();
603
+ this.approvalFailureDispositionByItem = /* @__PURE__ */ new Map();
604
+ this.preToolUseFailureByItem = /* @__PURE__ */ new Map();
605
+ this.finalized = false;
606
+ this.turnCompleted = false;
607
+ this.pendingMcpNotifications = 0;
608
+ }
609
+ recordNativeToolOutcome(item) {
610
+ if (!item || this.nativeToolOutcomeOrdinals.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
611
+ const ordinal = this.context.allocateToolOutcomeOrdinal?.(item.id);
612
+ if (ordinal !== void 0) this.nativeToolOutcomeOrdinals.set(item.id, ordinal);
613
+ }
614
+ clearTerminalPresentationForNativeItem(item) {
615
+ if (!item || this.terminalPresentationClearedItemIds.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
616
+ const toolCallOrdinal = this.nativeToolOutcomeOrdinals.get(item.id);
617
+ this.terminalPresentationClearedItemIds.add(item.id);
618
+ this.context.onToolOutcome?.({
619
+ toolName: itemName(item) ?? item.type,
620
+ argsHash: "",
621
+ resultHash: "",
622
+ ...toolCallOrdinal !== void 0 ? { toolCallOrdinal } : {},
623
+ terminalPresentation: void 0,
624
+ presentationOnly: true
625
+ });
626
+ }
627
+ getActiveMcpToolCall(serverName) {
628
+ if (this.finalized || this.turnCompleted || this.pendingMcpNotifications > 0 || this.options.runAbortSignal?.aborted) return;
629
+ let candidate;
630
+ for (const { mcpToolCall } of this.activeItems.values()) {
631
+ if (mcpToolCall?.server !== serverName) continue;
632
+ if (candidate) return;
633
+ candidate = mcpToolCall;
634
+ }
635
+ 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;
636
+ return {
637
+ id: candidate.id,
638
+ server: candidate.server,
639
+ tool: candidate.tool,
640
+ arguments: candidate.arguments
641
+ };
642
+ }
643
+ recordMcpToolCallReceipt(notification) {
644
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
645
+ if (!params || !isCodexNotificationForTurn(params, this.threadId, this.turnId)) return;
646
+ if (isMcpToolCallItemNotification(notification.method, params)) this.pendingMcpNotifications += 1;
647
+ else if (notification.method === "turn/completed" && readCodexTurn(params.turn)?.id === this.turnId) this.turnCompleted = true;
648
+ }
649
+ handleNotification(notification) {
650
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
651
+ if (!params || !isCodexNotificationForTurn(params, this.threadId, this.turnId)) return;
652
+ if (this.pendingMcpNotifications > 0 && isMcpToolCallItemNotification(notification.method, params)) this.pendingMcpNotifications -= 1;
653
+ if (notification.method === "turn/completed") {
654
+ const turn = readCodexTurn(params.turn);
655
+ if (!turn || turn.id !== this.turnId) return;
656
+ this.turnCompleted = true;
657
+ for (const item of turn.items ?? []) this.recordSnapshotItem(item);
658
+ return;
659
+ }
660
+ if (notification.method === "rawResponseItem/completed") {
661
+ const item = isJsonObject(params.item) ? params.item : void 0;
662
+ if (item) this.recordRawWebSearchResult(item);
663
+ return;
664
+ }
665
+ if (notification.method !== "item/started" && notification.method !== "item/completed") return;
666
+ const item = readItem(params.item);
667
+ if (!item) return;
668
+ this.recordItem({
669
+ phase: notification.method === "item/started" ? "start" : "result",
670
+ item,
671
+ sourceTimestampMs: asDateTimestampMs(notification.method === "item/started" ? params.startedAtMs : params.completedAtMs)
672
+ });
673
+ }
674
+ recordItem(params) {
675
+ const toolName = auditNativeToolName(params.item) ?? (params.item.type === "mcpToolCall" ? params.item.type : void 0);
676
+ if (!toolName || this.completedItemIds.has(params.item.id)) return;
677
+ if (params.phase === "start") {
678
+ this.recordStarted(params.item.id, toolName, auditNativeToolUnfinishedStatus(params.item), params.sourceTimestampMs, params.item.type === "mcpToolCall" ? params.item : void 0);
679
+ return;
680
+ }
681
+ if (params.item.type === "webSearch") {
682
+ this.webSearchCompletionByItem.set(params.item.id, {
683
+ runWasAborted: this.options.runAbortSignal?.aborted === true,
684
+ sourceTimestampMs: params.sourceTimestampMs
685
+ });
686
+ return;
687
+ }
688
+ const itemDurationMs = typeof params.item.durationMs === "number" ? params.item.durationMs : void 0;
689
+ this.recordTerminal(params.item.id, toolName, auditNativeToolTerminalStatus(params.item), {
690
+ itemDurationMs,
691
+ sourceTimestampMs: params.sourceTimestampMs
692
+ });
693
+ }
694
+ recordApprovalFailureDisposition(toolCallId, disposition) {
695
+ if (!this.completedItemIds.has(toolCallId)) this.approvalFailureDispositionByItem.set(toolCallId, disposition);
696
+ }
697
+ recordPreToolUseFailure(failure, runWasAborted = this.options.runAbortSignal?.aborted === true) {
698
+ if (this.completedItemIds.has(failure.toolCallId)) return;
699
+ const record = {
700
+ failure,
701
+ terminalReason: runWasAborted && this.options.runAbortSignal ? resolveCodexToolAbortTerminalReason(this.options.runAbortSignal) : failure.disposition
702
+ };
703
+ if (this.finalized) {
704
+ this.completedItemIds.add(failure.toolCallId);
705
+ this.emitPreToolUseFailure(record, failure.toolName, failure.durationMs);
706
+ return;
707
+ }
708
+ this.preToolUseFailureByItem.set(failure.toolCallId, record);
709
+ }
710
+ recordRawWebSearchResult(item) {
711
+ if (readStringField(item, "type") !== "web_search_call") return;
712
+ const toolCallId = readStringField(item, "id");
713
+ if (!toolCallId || this.completedItemIds.has(toolCallId)) return;
714
+ const toolName = "web_search";
715
+ this.recordStarted(toolCallId, toolName, "unknown");
716
+ const rawStatus = readStringField(item, "status");
717
+ if (rawStatus === "in_progress" || rawStatus === "running") return;
718
+ const status = rawStatus === "completed" ? "completed" : rawStatus === "cancelled" ? "cancelled" : rawStatus === "failed" || rawStatus === "error" || rawStatus === "incomplete" ? "failed" : "unknown";
719
+ this.recordTerminal(toolCallId, toolName, status, { sourceTimestampMs: this.webSearchCompletionByItem.get(toolCallId)?.sourceTimestampMs });
720
+ }
721
+ recordTerminal(toolCallId, toolName, status, options = {}) {
722
+ const runWasAborted = options.runWasAborted ?? this.options.runAbortSignal?.aborted === true;
723
+ const preToolUseFailure = this.preToolUseFailureByItem.get(toolCallId);
724
+ this.preToolUseFailureByItem.delete(toolCallId);
725
+ const approvalFailureDisposition = this.approvalFailureDispositionByItem.get(toolCallId);
726
+ this.approvalFailureDispositionByItem.delete(toolCallId);
727
+ this.completedItemIds.add(toolCallId);
728
+ this.activeItems.delete(toolCallId);
729
+ this.webSearchCompletionByItem.delete(toolCallId);
730
+ const startedAt = this.startedAtByItem.get(toolCallId);
731
+ this.startedAtByItem.delete(toolCallId);
732
+ const endedAt = options.sourceTimestampMs ?? Date.now();
733
+ const durationMs = options.itemDurationMs ?? (startedAt === void 0 ? 0 : Math.max(0, endedAt - startedAt));
734
+ if (preToolUseFailure) {
735
+ this.emitPreToolUseFailure(preToolUseFailure, toolName, durationMs, options.sourceTimestampMs);
736
+ return;
737
+ }
738
+ const terminalEvent = approvalFailureDisposition ? {
739
+ type: "tool.execution.error",
740
+ durationMs,
741
+ errorCategory: "codex_native_tool_approval",
742
+ terminalReason: approvalFailureDisposition
743
+ } : status === "blocked" ? {
744
+ type: "tool.execution.blocked",
745
+ reason: "codex_native_tool_blocked",
746
+ deniedReason: "codex_native_tool_blocked"
747
+ } : status === "failed" || status === "cancelled" || status === "unknown" ? {
748
+ type: "tool.execution.error",
749
+ durationMs,
750
+ errorCategory: status === "unknown" ? "codex_native_tool_outcome_unknown" : status === "cancelled" ? "aborted" : "codex_native_tool_error",
751
+ ...status === "unknown" ? { errorCode: "tool_outcome_unknown" } : {},
752
+ terminalReason: status === "unknown" ? "failed" : runWasAborted && this.options.runAbortSignal ? resolveCodexToolAbortTerminalReason(this.options.runAbortSignal) : status === "cancelled" ? "cancelled" : "failed"
753
+ } : {
754
+ type: "tool.execution.completed",
755
+ durationMs
756
+ };
757
+ emitTrustedDiagnosticEvent({
758
+ ...this.buildBase(toolCallId, toolName),
759
+ ...terminalEvent,
760
+ ...options.sourceTimestampMs !== void 0 ? { sourceTimestampMs: options.sourceTimestampMs } : {}
761
+ });
762
+ }
763
+ finalizeActive(runWasAborted = this.options.runAbortSignal?.aborted === true) {
764
+ this.finalized = true;
765
+ for (const [toolCallId, { toolName, unfinishedStatus }] of this.activeItems) {
766
+ const webSearchCompletion = this.webSearchCompletionByItem.get(toolCallId);
767
+ const itemRunWasAborted = webSearchCompletion ? webSearchCompletion.runWasAborted : runWasAborted;
768
+ this.recordTerminal(toolCallId, toolName, unfinishedStatus, {
769
+ runWasAborted: itemRunWasAborted,
770
+ sourceTimestampMs: webSearchCompletion?.sourceTimestampMs
771
+ });
772
+ }
773
+ for (const [toolCallId, record] of this.preToolUseFailureByItem) if (!this.completedItemIds.has(toolCallId)) this.recordTerminal(toolCallId, record.failure.toolName, "failed", { itemDurationMs: record.failure.durationMs });
774
+ this.activeItems.clear();
775
+ this.webSearchCompletionByItem.clear();
776
+ this.approvalFailureDispositionByItem.clear();
777
+ this.preToolUseFailureByItem.clear();
778
+ }
779
+ emitPreToolUseFailure(record, toolName, durationMs, sourceTimestampMs) {
780
+ emitCodexNativePreToolUseFailureDiagnostic({
781
+ agentId: this.context.agentId,
782
+ sessionId: this.context.sessionId,
783
+ sessionKey: this.context.sessionKey,
784
+ runId: this.context.runId,
785
+ failure: {
786
+ ...record.failure,
787
+ toolName,
788
+ durationMs
789
+ },
790
+ terminalReason: record.terminalReason,
791
+ sourceTimestampMs
792
+ });
793
+ }
794
+ recordSnapshotItem(item) {
795
+ if (!auditNativeToolName(item) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
796
+ const toolName = auditNativeToolName(item);
797
+ if (!toolName) return;
798
+ this.recordStarted(item.id, toolName, auditNativeToolUnfinishedStatus(item));
799
+ this.recordItem({
800
+ phase: "result",
801
+ item
802
+ });
803
+ }
804
+ recordStarted(toolCallId, toolName, unfinishedStatus, sourceTimestampMs, mcpToolCall) {
805
+ if (this.activeItems.has(toolCallId)) return;
806
+ this.startedAtByItem.set(toolCallId, sourceTimestampMs ?? Date.now());
807
+ this.activeItems.set(toolCallId, {
808
+ toolName,
809
+ unfinishedStatus,
810
+ mcpToolCall
811
+ });
812
+ emitTrustedDiagnosticEvent({
813
+ type: "tool.execution.started",
814
+ ...this.buildBase(toolCallId, toolName),
815
+ ...sourceTimestampMs !== void 0 ? { sourceTimestampMs } : {}
816
+ });
817
+ }
818
+ buildBase(toolCallId, toolName) {
819
+ return {
820
+ agentId: this.context.agentId,
821
+ runId: this.context.runId,
822
+ sessionId: this.context.sessionId,
823
+ sessionKey: this.context.sessionKey,
824
+ toolName,
825
+ toolCallId
826
+ };
827
+ }
828
+ };
829
+ //#endregion
830
+ //#region extensions/codex/src/app-server/remote-workspace-media.ts
831
+ const REMOTE_WORKSPACE_MEDIA_TIMEOUT_MS = 6e4;
832
+ const REMOTE_WORKSPACE_MEDIA_MAX_BYTES = 67108864;
833
+ const REMOTE_WORKSPACE_MEDIA_MAX_ATTACHMENTS = 16;
834
+ const CODEX_REMOTE_MEDIA_CHUNK_BYTES = 524288;
835
+ const CODEX_REMOTE_COMMAND_DEFAULT_OUTPUT_BYTES = 1048576;
836
+ const CODEX_BOUNDED_REMOTE_FILE_READER = [
837
+ "try{",
838
+ "const fs=require(\"node:fs\");",
839
+ "const path=require(\"node:path\");",
840
+ "const file=process.argv[1];",
841
+ "const max=Number(process.argv[2]);",
842
+ "const offset=Number(process.argv[3]);",
843
+ "const chunk=Number(process.argv[4]);",
844
+ "const workspace=process.argv[5];",
845
+ "if(!Number.isSafeInteger(max)||max<0)throw Error(\"invalid media byte limit\");",
846
+ "if(!Number.isSafeInteger(offset)||offset<0||!Number.isSafeInteger(chunk)||chunk<=0)throw Error(\"invalid media chunk\");",
847
+ "if(fs.lstatSync(file).isSymbolicLink())throw Error(\"symbolic links are not allowed\");",
848
+ "const noFollow=fs.constants.O_NOFOLLOW??0;",
849
+ "const fd=fs.openSync(file,fs.constants.O_RDONLY|noFollow);",
850
+ "try{",
851
+ "const before=fs.fstatSync(fd);",
852
+ "if(!before.isFile())throw Error(\"not a regular file\");",
853
+ "if(workspace){",
854
+ "const descriptor=process.platform===\"linux\"?fs.realpathSync(`/proc/self/fd/${fd}`):fs.realpathSync(file);",
855
+ "const relative=path.relative(fs.realpathSync(workspace),descriptor);",
856
+ "if(!relative||relative===\"..\"||relative.startsWith(`..${path.sep}`)||path.isAbsolute(relative))throw Error(\"file escapes remote workspace\");",
857
+ "const verified=fs.statSync(descriptor);",
858
+ "if(verified.dev!==before.dev||verified.ino!==before.ino)throw Error(\"file changed while being opened\");",
859
+ "}",
860
+ "if(before.size>max)throw Error(`file exceeds limit of ${max} bytes`);",
861
+ "if(offset>before.size)throw Error(\"file changed while being read\");",
862
+ "const expected=Math.min(chunk,before.size-offset);",
863
+ "const buffer=Buffer.allocUnsafe(expected);",
864
+ "let total=0;",
865
+ "while(total<buffer.length){",
866
+ "const count=fs.readSync(fd,buffer,total,buffer.length-total,offset+total);",
867
+ "if(count===0)break;total+=count;",
868
+ "}",
869
+ "if(total!==expected)throw Error(\"file changed while being read\");",
870
+ "const after=fs.fstatSync(fd);",
871
+ "const revision=stat=>[stat.dev,stat.ino,stat.size,stat.mtimeMs,stat.ctimeMs].join(\":\");",
872
+ "if(!after.isFile()||revision(after)!==revision(before))throw Error(\"file changed while being read\");",
873
+ "process.stdout.write(JSON.stringify({dataBase64:buffer.toString(\"base64\"),size:before.size,revision:revision(before)}));",
874
+ "}finally{fs.closeSync(fd)}",
875
+ "}catch(error){process.stderr.write(error instanceof Error?error.message:String(error));process.exitCode=1}"
876
+ ].join("");
877
+ const MESSAGE_MEDIA_KEYS = [
878
+ "media",
879
+ "mediaUrl",
880
+ "media_url",
881
+ "path",
882
+ "filePath",
883
+ "fileUrl",
884
+ "imageUrl",
885
+ "image_url"
886
+ ];
887
+ const MESSAGE_MEDIA_ARRAY_KEYS = [
888
+ "mediaUrls",
889
+ "media_urls",
890
+ "imageUrls",
891
+ "image_urls"
892
+ ];
893
+ const ATTACHMENT_MEDIA_KEYS = [
894
+ "media",
895
+ "mediaUrl",
896
+ "path",
897
+ "filePath",
898
+ "fileUrl",
899
+ "url"
900
+ ];
901
+ /** Reads actual remote bytes with a cap enforced by Codex before transport. */
902
+ async function readBoundedCodexRemoteWorkspaceFile(params) {
903
+ if (!Number.isSafeInteger(params.maxBytes) || params.maxBytes < 0) throw new Error("Codex remote workspace upload requires a valid media byte limit.");
904
+ params.signal?.throwIfAborted();
905
+ const chunks = [];
906
+ let offset = 0;
907
+ let expectedSize;
908
+ let expectedRevision;
909
+ const startedAt = Date.now();
910
+ do {
911
+ params.signal?.throwIfAborted();
912
+ const timeoutMs = params.timeoutMs === void 0 ? void 0 : params.timeoutMs - (Date.now() - startedAt);
913
+ if (timeoutMs !== void 0 && timeoutMs <= 0) throw new Error("Codex remote workspace file transfer timed out.");
914
+ let response;
915
+ try {
916
+ response = await params.client.request("command/exec", {
917
+ command: [
918
+ "node",
919
+ "-e",
920
+ CODEX_BOUNDED_REMOTE_FILE_READER,
921
+ "--",
922
+ params.path,
923
+ String(params.maxBytes),
924
+ String(offset),
925
+ String(CODEX_REMOTE_MEDIA_CHUNK_BYTES),
926
+ ...params.workspaceRoot ? [params.workspaceRoot] : []
927
+ ],
928
+ env: {
929
+ NODE_OPTIONS: null,
930
+ NODE_PATH: null
931
+ },
932
+ ...timeoutMs === void 0 ? {} : { timeoutMs }
933
+ }, {
934
+ signal: params.signal,
935
+ timeoutMs
936
+ });
937
+ } catch (error) {
938
+ if (error instanceof Error && /failed to spawn|executable.*not found|\bENOENT\b/iu.test(error.message)) throw new Error("Codex remote workspace file transfer requires Node.js on the remote app-server host.", { cause: error });
939
+ throw error;
940
+ }
941
+ if (!response || response.exitCode !== 0) {
942
+ const detail = typeof response?.stderr === "string" ? response.stderr.trim() : "";
943
+ throw new Error(`Codex remote workspace artifact could not be read: ${params.path}${detail ? `: ${detail}` : ""}`);
944
+ }
945
+ if (typeof response.stdout !== "string" || response.stdout.length > CODEX_REMOTE_COMMAND_DEFAULT_OUTPUT_BYTES) throw new Error("Codex remote workspace artifact exceeded the native command output cap.");
946
+ let payload;
947
+ try {
948
+ payload = JSON.parse(response.stdout);
949
+ } catch {
950
+ throw new Error("Codex remote workspace artifact returned invalid chunk data.");
951
+ }
952
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) throw new Error("Codex remote workspace artifact returned invalid chunk data.");
953
+ const chunk = payload;
954
+ if (typeof chunk.dataBase64 !== "string" || !Number.isSafeInteger(chunk.size) || chunk.size < 0 || chunk.size > params.maxBytes || typeof chunk.revision !== "string" || !chunk.revision) throw new Error("Codex remote workspace artifact returned invalid or oversized chunk data.");
955
+ if (expectedSize === void 0) {
956
+ expectedSize = chunk.size;
957
+ expectedRevision = chunk.revision;
958
+ }
959
+ if (chunk.size !== expectedSize || chunk.revision !== expectedRevision) throw new Error("Codex remote workspace artifact changed during chunked transfer.");
960
+ const remainingBytes = expectedSize - offset;
961
+ const expectedChunkBytes = Math.min(CODEX_REMOTE_MEDIA_CHUNK_BYTES, remainingBytes);
962
+ if (chunk.dataBase64.length > Math.ceil(expectedChunkBytes / 3) * 4) throw new Error("Codex remote workspace artifact returned oversized chunk data.");
963
+ const buffer = Buffer.from(chunk.dataBase64, "base64");
964
+ if (buffer.byteLength !== expectedChunkBytes || buffer.toString("base64") !== chunk.dataBase64) throw new Error("Codex remote workspace artifact returned invalid chunk data.");
965
+ chunks.push(buffer);
966
+ offset += buffer.byteLength;
967
+ } while (offset < (expectedSize ?? 0));
968
+ return { dataBase64: Buffer.concat(chunks, offset).toString("base64") };
1037
969
  }
1038
- /** Adds sandbox_exec/process aliases when native Code Mode cannot directly honor the sandbox. */
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;
1041
- const execTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "exec");
1042
- const processTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "process");
1043
- if (!execTool || !processTool) return filteredTools;
1044
- const sandboxExecTool = {
1045
- ...execTool,
1046
- name: "sandbox_exec",
1047
- description: "Run a shell command through OpenClaw's configured sandbox backend for this session. Use when OpenClaw sandboxing is active or when a command must execute in the sandbox backend, such as an SSH-backed sandbox or Docker container-path bind layout. Use Codex's native shell only when no OpenClaw sandbox is active and native Code Mode is available.",
1048
- execute: async (toolCallId, args, signal, onUpdate) => {
1049
- const result = await execTool.execute(toolCallId, args, signal, onUpdate);
1050
- return {
1051
- ...result,
1052
- content: result.content.map((item) => item.type === "text" ? Object.assign({}, item, { text: item.text.replace("Use process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.", "Use sandbox_process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.") }) : item)
1053
- };
970
+ /** Stages authoritative bounded remote bytes into immutable Gateway-owned media. */
971
+ async function prepareCodexRemoteWorkspaceMessageMedia(params) {
972
+ const { localWorkspaceRoot, remoteWorkspaceRoot } = params;
973
+ if (!localWorkspaceRoot || !remoteWorkspaceRoot) return params.args;
974
+ const remotePathsByLocalPath = /* @__PURE__ */ new Map();
975
+ const gatewayManagedPaths = /* @__PURE__ */ new Set();
976
+ const gatewayMediaRoot = getMediaDir();
977
+ let attachmentEntries = 0;
978
+ const mapMediaPath = (value) => {
979
+ if (typeof value !== "string") return value;
980
+ if (path.isAbsolute(value) && isPathStrictlyInside(gatewayMediaRoot, value)) {
981
+ attachmentEntries += 1;
982
+ gatewayManagedPaths.add(value);
983
+ return value;
984
+ }
985
+ const mapped = mapCodexAppServerLocalWorkspacePath({
986
+ value,
987
+ localWorkspaceRoot,
988
+ remoteWorkspaceRoot
989
+ });
990
+ if (value.trim() && !isCodexPassThroughMediaSource(value)) {
991
+ attachmentEntries += 1;
992
+ remotePathsByLocalPath.set(mapped, mapCodexAppServerRemoteWorkspacePath({
993
+ value: mapped,
994
+ localWorkspaceRoot,
995
+ remoteWorkspaceRoot
996
+ }));
1054
997
  }
998
+ return mapped;
1055
999
  };
1056
- const sandboxProcessTool = {
1057
- ...processTool,
1058
- name: "sandbox_process",
1059
- description: "Manage background shell sessions through OpenClaw's configured sandbox backend for this session: list, poll, log, write, send-keys, submit, paste, kill, clear, or remove. Use only for sandbox follow-up; use Codex's native shell session handling only when no OpenClaw sandbox is active and native Code Mode is available."
1000
+ let mappedArgs = params.args;
1001
+ const setMappedValue = (key, value) => {
1002
+ if (value === params.args[key]) return;
1003
+ if (mappedArgs === params.args) mappedArgs = { ...params.args };
1004
+ mappedArgs[key] = value;
1060
1005
  };
1061
- return [
1062
- ...filteredTools,
1063
- sandboxExecTool,
1064
- sandboxProcessTool
1065
- ];
1066
- }
1067
- function isSandboxShellDynamicToolExcluded(config) {
1068
- return isCodexDynamicToolExcluded(config, [
1069
- "exec",
1070
- "sandbox_exec",
1071
- "process",
1072
- "sandbox_process"
1073
- ]);
1074
- }
1075
- async function addNodeShellDynamicToolsIfNeeded(filteredTools, allTools, input, nodePolicy) {
1076
- if (isCodexMemoryFlushRun(input.params)) return filteredTools;
1077
- const nodeExecIsDefault = nodePolicy.effectiveExecHost === "node";
1078
- const nodeExecAvailableFromAuto = nodePolicy.requestedExecHost === "auto" && nodePolicy.effectiveExecHost === "gateway";
1079
- if (!nodeExecIsDefault && !nodeExecAvailableFromAuto) return filteredTools;
1080
- const execTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "exec");
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;
1084
- }
1085
- function shouldKeepOpenClawShellDynamicTools(input, nodePolicy) {
1086
- return !isCodexMemoryFlushRun(input.params) && input.nativeToolSurfaceEnabled === false && input.sandbox?.enabled !== true && nodePolicy.effectiveExecHost !== "node";
1006
+ for (const key of MESSAGE_MEDIA_KEYS) setMappedValue(key, mapMediaPath(params.args[key]));
1007
+ for (const key of MESSAGE_MEDIA_ARRAY_KEYS) {
1008
+ const value = params.args[key];
1009
+ if (Array.isArray(value)) {
1010
+ const mapped = value.map(mapMediaPath);
1011
+ if (mapped.some((entry, index) => entry !== value[index])) setMappedValue(key, mapped);
1012
+ }
1013
+ }
1014
+ if (Array.isArray(params.args.attachments)) {
1015
+ const attachments = params.args.attachments;
1016
+ const mapped = attachments.map((attachment) => {
1017
+ if (!attachment || typeof attachment !== "object" || Array.isArray(attachment)) return attachment;
1018
+ const record = attachment;
1019
+ let mappedAttachment = record;
1020
+ for (const key of ATTACHMENT_MEDIA_KEYS) {
1021
+ const value = mapMediaPath(record[key]);
1022
+ if (value !== record[key]) {
1023
+ if (mappedAttachment === record) mappedAttachment = { ...record };
1024
+ mappedAttachment[key] = value;
1025
+ }
1026
+ }
1027
+ return mappedAttachment;
1028
+ });
1029
+ if (mapped.some((attachment, index) => attachment !== attachments[index])) setMappedValue("attachments", mapped);
1030
+ }
1031
+ if (attachmentEntries > REMOTE_WORKSPACE_MEDIA_MAX_ATTACHMENTS) throw new Error(`Codex remote workspace upload exceeds the ${REMOTE_WORKSPACE_MEDIA_MAX_ATTACHMENTS}-attachment limit.`);
1032
+ for (const managedPath of gatewayManagedPaths) await assertGatewayManagedMediaPath(managedPath, gatewayMediaRoot);
1033
+ if (remotePathsByLocalPath.size === 0) return mappedArgs;
1034
+ const readRemoteFile = params.readRemoteFile;
1035
+ if (!readRemoteFile) throw new Error("Codex remote workspace file transfer requires an active app-server client.");
1036
+ const maxBytes = params.maxBytes ?? REMOTE_WORKSPACE_MEDIA_MAX_BYTES;
1037
+ const timeoutMs = params.timeoutMs ?? REMOTE_WORKSPACE_MEDIA_TIMEOUT_MS;
1038
+ const deadline = Date.now() + timeoutMs;
1039
+ const stagedPaths = /* @__PURE__ */ new Map();
1040
+ let totalBytes = 0;
1041
+ for (const [localPath, remotePath] of remotePathsByLocalPath) {
1042
+ params.signal?.throwIfAborted();
1043
+ const remainingBytes = maxBytes - totalBytes;
1044
+ const remainingMs = deadline - Date.now();
1045
+ if (remainingMs <= 0) throw new Error("Codex remote workspace attachment batch timed out.");
1046
+ const response = await readRemoteFile({
1047
+ path: remotePath,
1048
+ maxBytes: remainingBytes,
1049
+ workspaceRoot: remoteWorkspaceRoot,
1050
+ signal: params.signal,
1051
+ timeoutMs: remainingMs
1052
+ });
1053
+ if (!response || typeof response.dataBase64 !== "string") throw new Error(`Codex remote workspace artifact returned no file data: ${remotePath}`);
1054
+ if (response.dataBase64.length > Math.ceil(remainingBytes / 3) * 4) throw new Error(`Codex remote workspace artifact exceeds the limit of ${remainingBytes} bytes.`);
1055
+ const remoteBuffer = Buffer.from(response.dataBase64, "base64");
1056
+ if (remoteBuffer.byteLength > remainingBytes || remoteBuffer.toString("base64") !== response.dataBase64) throw new Error(`Codex remote workspace artifact returned invalid or oversized file data: ${remotePath}`);
1057
+ totalBytes += remoteBuffer.byteLength;
1058
+ const saved = await saveMediaBuffer(remoteBuffer, void 0, "outbound", maxBytes, path.basename(remotePath));
1059
+ stagedPaths.set(localPath, saved.path);
1060
+ }
1061
+ return mapMessageMediaValues(mappedArgs, (value) => stagedPaths.get(value) ?? value);
1087
1062
  }
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;
1063
+ async function assertGatewayManagedMediaPath(value, mediaRoot) {
1064
+ const opened = await (await root(mediaRoot, { symlinks: "reject" })).open(path.relative(mediaRoot, value), { symlinks: "reject" });
1065
+ try {
1066
+ if (!(await opened.handle.stat()).isFile()) throw new Error(`Codex Gateway-managed media is not a regular file: ${value}`);
1067
+ } finally {
1068
+ await opened[Symbol.asyncDispose]();
1069
+ }
1093
1070
  }
1094
- /** Applies a normalized tool allowlist while preserving shell aliases for exec/process. */
1095
- function filterCodexDynamicToolsForAllowlist(tools, toolsAllow) {
1096
- if (!toolsAllow) return tools;
1097
- if (toolsAllow.length === 0) return [];
1098
- if (hasWildcardCodexToolsAllow(toolsAllow)) return tools;
1099
- const allowSet = new Set(toolsAllow.map((name) => normalizeCodexDynamicToolName(name)).filter(Boolean));
1100
- return tools.filter((tool) => {
1101
- const normalized = normalizeCodexDynamicToolName(tool.name);
1102
- return allowSet.has(normalized) || normalized === "sandbox_exec" && allowSet.has("exec") || normalized === "sandbox_process" && (allowSet.has("exec") || allowSet.has("process")) || normalized === "gateway_exec" && allowSet.has("exec") || normalized === "gateway_process" && (allowSet.has("exec") || allowSet.has("process")) || normalized === "node_exec" && allowSet.has("exec");
1071
+ function mapMessageMediaValues(args, mapValue) {
1072
+ const mapped = { ...args };
1073
+ for (const key of MESSAGE_MEDIA_KEYS) {
1074
+ const value = mapped[key];
1075
+ if (typeof value === "string") mapped[key] = mapValue(value);
1076
+ }
1077
+ for (const key of MESSAGE_MEDIA_ARRAY_KEYS) {
1078
+ const value = mapped[key];
1079
+ if (Array.isArray(value)) mapped[key] = value.map((entry) => typeof entry === "string" ? mapValue(entry) : entry);
1080
+ }
1081
+ if (Array.isArray(mapped.attachments)) mapped.attachments = mapped.attachments.map((attachment) => {
1082
+ if (!attachment || typeof attachment !== "object" || Array.isArray(attachment)) return attachment;
1083
+ const record = { ...attachment };
1084
+ for (const key of ATTACHMENT_MEDIA_KEYS) {
1085
+ const value = record[key];
1086
+ if (typeof value === "string") record[key] = mapValue(value);
1087
+ }
1088
+ return record;
1103
1089
  });
1104
- }
1105
- /** Detects the wildcard allowlist marker after Codex tool-name normalization. */
1106
- function hasWildcardCodexToolsAllow(toolsAllow) {
1107
- return toolsAllow.some((name) => normalizeCodexDynamicToolName(name) === "*");
1108
- }
1109
- /** Forces message delivery through the message tool when the source channel requires it. */
1110
- function shouldForceMessageTool(params) {
1111
- return params.disableMessageTool !== true && params.sourceReplyDeliveryMode === "message_tool_only";
1090
+ return mapped;
1112
1091
  }
1113
1092
  //#endregion
1114
1093
  //#region extensions/codex/src/app-server/sandbox-exec-server-node-relay.ts
@@ -1140,7 +1119,7 @@ const CODEX_NODE_HTTP_CREDENTIAL_FIELD_NAME_PATTERN = /^(?:(?:[a-z\d]+_)*(?:toke
1140
1119
  const nodeExecServerTextDecoder = new TextDecoder("utf-8", { fatal: true });
1141
1120
  /** Produces the bounded, redacted terminal failure shared by pending and claimed node leases. */
1142
1121
  function createCodexNodeExecServerDisconnectError(reason, cause) {
1143
- const detail = cause === void 0 ? "" : `: ${truncateUtf16Safe(redactSensitiveText(formatErrorMessage$1(cause), { mode: "tools" }), CODEX_NODE_EXEC_SERVER_MAX_FAILURE_DETAIL_CHARS)}`;
1122
+ const detail = cause === void 0 ? "" : `: ${truncateUtf16Safe(redactSensitiveText(formatErrorMessage$2(cause), { mode: "tools" }), CODEX_NODE_EXEC_SERVER_MAX_FAILURE_DETAIL_CHARS)}`;
1144
1123
  return /* @__PURE__ */ new Error(`Codex execution node disconnected; start a fresh attempt. (${reason}${detail})`);
1145
1124
  }
1146
1125
  /** Relays one authorized, single-use Codex exec-server channel without interpreting its protocol. */
@@ -1434,6 +1413,10 @@ const sandboxExecServerRegistry = {
1434
1413
  }
1435
1414
  };
1436
1415
  //#endregion
1416
+ //#region extensions/codex/src/app-server/sandbox-exec-server.websocket.ts
1417
+ const require = createRequire(import.meta.url);
1418
+ const websocket = require(path.join(path.dirname(require.resolve("ws/package.json")), "index.js"));
1419
+ //#endregion
1437
1420
  //#region extensions/codex/src/app-server/sandbox-exec-server/json-rpc.ts
1438
1421
  /** JSON-RPC error code used when a sandbox filesystem resource does not exist. */
1439
1422
  const JSON_RPC_NOT_FOUND = -32004;
@@ -3112,7 +3095,7 @@ async function startOpenClawExecServer(sandbox) {
3112
3095
  fsBridge
3113
3096
  };
3114
3097
  }
3115
- const server = new WebSocketServer({
3098
+ const server = new websocket.WebSocketServer({
3116
3099
  host: "127.0.0.1",
3117
3100
  port: 0,
3118
3101
  maxPayload: connection.kind === "node" ? CODEX_NODE_EXEC_SERVER_MAX_MESSAGE_BYTES : CODEX_SANDBOX_EXEC_SERVER_MAX_INBOUND_MESSAGE_BYTES
@@ -4686,6 +4669,7 @@ const CODEX_DYNAMIC_TOOL_NAME_MAX_CHARS = 128;
4686
4669
  const CODEX_DYNAMIC_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/u;
4687
4670
  const ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES = /* @__PURE__ */ new Set([
4688
4671
  "agents_list",
4672
+ "agents_wait",
4689
4673
  "progress_card",
4690
4674
  "sessions_spawn",
4691
4675
  "sessions_yield"
@@ -4864,6 +4848,34 @@ function readCodexDynamicToolDescriptor(tool, toolIndex) {
4864
4848
  };
4865
4849
  }
4866
4850
  //#endregion
4851
+ //#region extensions/codex/src/app-server/dynamic-tool-availability.ts
4852
+ /** Prepare the actual executable set, then reproject only changed owner definitions. */
4853
+ function finalizeCodexToolAvailability(entries) {
4854
+ const snapshots = entries.map((entry) => ({
4855
+ entry,
4856
+ parameters: entry.tool.parameters,
4857
+ description: entry.tool.description
4858
+ }));
4859
+ const preparedNames = /* @__PURE__ */ new Set();
4860
+ finalizeAgentToolAvailability(entries.map((entry) => entry.tool), { onPrepared: (tool) => preparedNames.add(tool.name) });
4861
+ const tools = [];
4862
+ const quarantinedTools = [];
4863
+ for (const { entry, parameters, description } of snapshots) {
4864
+ if (entry.tool.parameters === parameters && entry.tool.description === description) {
4865
+ tools.push(entry);
4866
+ continue;
4867
+ }
4868
+ const projected = projectCodexDynamicTools([entry.tool]);
4869
+ tools.push(...projected.tools);
4870
+ quarantinedTools.push(...projected.quarantinedTools);
4871
+ }
4872
+ return {
4873
+ tools,
4874
+ quarantinedTools,
4875
+ preparedNames
4876
+ };
4877
+ }
4878
+ //#endregion
4867
4879
  //#region extensions/codex/src/app-server/dynamic-tools.ts
4868
4880
  /**
4869
4881
  * Bridges OpenClaw runtime tools into Codex app-server dynamic tool specs and
@@ -5068,18 +5080,21 @@ function createCodexDynamicToolBridge(params) {
5068
5080
  const registeredProjection = params.registeredTools ? projectCodexDynamicTools(params.registeredTools) : availableProjection;
5069
5081
  const inheritedSpecs = params.registeredSpecs ? structuredClone([...params.registeredSpecs]) : void 0;
5070
5082
  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));
5072
- const quarantinedAvailableToolNames = new Set(availableProjection.quarantinedTools.map((tool) => tool.tool));
5073
- const registeredSpecTools = (params.registeredTools ? registeredProjection.tools : availableTools).filter((entry) => !quarantinedAvailableToolNames.has(entry.name));
5083
+ const registrationNames = inheritedNames ?? new Set(registeredProjection.tools.map((entry) => entry.name));
5084
+ const finalized = finalizeCodexToolAvailability(availableProjection.tools.filter((entry) => registrationNames.has(entry.name)));
5085
+ const availableTools = finalized.tools;
5086
+ availableProjection.quarantinedTools.push(...finalized.quarantinedTools);
5074
5087
  const toolMap = new Map(availableTools.map((entry) => [entry.name, entry]));
5088
+ const quarantinedAvailableToolNames = new Set(availableProjection.quarantinedTools.map((tool) => tool.tool));
5089
+ const registeredSpecTools = (params.registeredTools ? registeredProjection.tools : availableTools).filter((entry) => !quarantinedAvailableToolNames.has(entry.name)).map((entry) => finalized.preparedNames.has(entry.name) ? toolMap.get(entry.name) ?? entry : entry);
5075
5090
  const registeredToolNames = inheritedNames ?? new Set(registeredSpecTools.map((entry) => entry.name));
5076
5091
  const quarantinedTools = dedupeQuarantinedDynamicTools([...availableProjection.quarantinedTools, ...registeredProjection.quarantinedTools]);
5077
- warnQuarantinedDynamicTools({
5092
+ reportQuarantinedDynamicTools({
5078
5093
  tools: quarantinedTools,
5079
5094
  availableToolCount: availableTools.length,
5080
- registeredToolCount: registeredSpecTools.length
5095
+ registeredToolCount: registeredSpecTools.length,
5096
+ hookContext: params.hookContext
5081
5097
  });
5082
- emitQuarantinedDynamicToolDiagnostics(quarantinedTools, params.hookContext);
5083
5098
  const telemetry = {
5084
5099
  didSendViaMessagingTool: false,
5085
5100
  didDeliverSourceReplyViaMessageTool: false,
@@ -5222,6 +5237,7 @@ function createCodexDynamicToolBridge(params) {
5222
5237
  }));
5223
5238
  const rawResult = await Reflect.apply(tool.execute, tool, executionArgs);
5224
5239
  captureExecutionBoundary();
5240
+ if (toolName === "message" && asOptionalRecord(asOptionalRecord(rawResult.details)?.messageDelivery)?.sourceReplyDelivered === true) telemetry.sourceReplyDelivered = true;
5225
5241
  const telemetryRawResult = sanitizeToolResult(rawResult);
5226
5242
  const rawIsError = isToolResultError(rawResult);
5227
5243
  const rawResultFailureKind = resolveToolResultFailureKind(rawResult);
@@ -5330,6 +5346,7 @@ function createCodexDynamicToolBridge(params) {
5330
5346
  const asyncStarted = isAsyncStartedToolResult(rawResult) || isAsyncStartedToolResult(result);
5331
5347
  withDynamicToolAsyncStarted(response, asyncStarted);
5332
5348
  const replaySafe = executionPrevented || !asyncStarted && isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs);
5349
+ copyInternalToolResultState(rawResult, response);
5333
5350
  return withDynamicToolExecutionState(response, {
5334
5351
  executedArguments: executedArgs,
5335
5352
  executionStarted: didStartExecution && !executionPrevented,
@@ -5386,19 +5403,36 @@ function createCodexDynamicToolBridge(params) {
5386
5403
  };
5387
5404
  }
5388
5405
  function projectCodexExecutableDynamicToolSurface(tools, hookContext) {
5389
- const projected = projectCodexDynamicTools(tools);
5390
- const wrapped = wrapProjectedCodexDynamicTools(projected.tools, hookContext);
5406
+ const { tools: projectedTools, quarantinedTools } = projectCodexDynamicTools(tools);
5407
+ const wrappedTools = [];
5408
+ for (const entry of projectedTools) try {
5409
+ if (isToolWrappedWithBeforeToolCallHook(entry.tool)) {
5410
+ setBeforeToolCallDiagnosticsEnabled(entry.tool, false);
5411
+ wrappedTools.push(entry);
5412
+ continue;
5413
+ }
5414
+ wrappedTools.push({
5415
+ ...entry,
5416
+ tool: wrapToolWithBeforeToolCallHook(entry.tool, hookContext, { emitDiagnostics: false })
5417
+ });
5418
+ } catch {
5419
+ quarantinedTools.push({
5420
+ tool: entry.name,
5421
+ violations: [`${entry.name} could not be wrapped for before-tool-call hooks`]
5422
+ });
5423
+ }
5391
5424
  return {
5392
- tools: wrapped.tools,
5393
- quarantinedTools: dedupeQuarantinedDynamicTools([...projected.quarantinedTools, ...wrapped.quarantinedTools])
5425
+ tools: wrappedTools,
5426
+ quarantinedTools: dedupeQuarantinedDynamicTools(quarantinedTools)
5394
5427
  };
5395
5428
  }
5396
5429
  /** Applies the exact schema and hook-wrapper projection used by the executable Codex bridge. */
5397
5430
  function projectCodexExecutableDynamicTools(params) {
5398
5431
  const projected = projectCodexExecutableDynamicToolSurface(params.tools, params.hookContext);
5432
+ const finalized = finalizeCodexToolAvailability(projected.tools);
5399
5433
  return {
5400
- availableTools: projected.tools.map((entry) => entry.tool),
5401
- quarantinedTools: projected.quarantinedTools
5434
+ availableTools: finalized.tools.map((entry) => entry.tool),
5435
+ quarantinedTools: [...projected.quarantinedTools, ...finalized.quarantinedTools]
5402
5436
  };
5403
5437
  }
5404
5438
  function notifyAgentToolResult(observer, toolName, result, isError) {
@@ -5409,7 +5443,8 @@ function notifyAgentToolResult(observer, toolName, result, isError) {
5409
5443
  isError
5410
5444
  });
5411
5445
  } catch (error) {
5412
- embeddedAgentLog.warn(`onAgentToolResult handler failed: tool=${toolName} error=${String(error)}`);
5446
+ const message = formatToolExecutionErrorMessage(error, "Unknown error");
5447
+ embeddedAgentLog.warn(`onAgentToolResult handler failed: tool=${toolName} error=${message}`);
5413
5448
  }
5414
5449
  }
5415
5450
  function failedToolResult(message, status = "failed") {
@@ -5424,50 +5459,22 @@ function failedToolResult(message, status = "failed") {
5424
5459
  }
5425
5460
  };
5426
5461
  }
5427
- function wrapProjectedCodexDynamicTools(tools, hookContext) {
5428
- const wrappedTools = [];
5429
- const quarantinedTools = [];
5430
- for (const entry of tools) try {
5431
- if (isToolWrappedWithBeforeToolCallHook(entry.tool)) {
5432
- setBeforeToolCallDiagnosticsEnabled(entry.tool, false);
5433
- wrappedTools.push(entry);
5434
- continue;
5435
- }
5436
- wrappedTools.push({
5437
- ...entry,
5438
- tool: wrapToolWithBeforeToolCallHook(entry.tool, hookContext, { emitDiagnostics: false })
5439
- });
5440
- } catch {
5441
- quarantinedTools.push({
5442
- tool: entry.name,
5443
- violations: [`${entry.name} could not be wrapped for before-tool-call hooks`]
5444
- });
5445
- }
5446
- return {
5447
- tools: wrappedTools,
5448
- quarantinedTools
5449
- };
5450
- }
5451
- function warnQuarantinedDynamicTools(params) {
5462
+ function reportQuarantinedDynamicTools(params) {
5452
5463
  if (params.tools.length === 0) return;
5453
- const unique = /* @__PURE__ */ new Map();
5454
- for (const tool of params.tools) unique.set(tool.tool, tool.violations);
5455
- embeddedAgentLog.warn(`codex app-server quarantined ${unique.size} unsupported dynamic tool ${unique.size === 1 ? "definition" : "definitions"}: ${[...unique.keys()].join(", ")}; retained ${params.availableToolCount} available and ${params.registeredToolCount} registered tools`, {
5456
- tools: [...unique.entries()].map(([tool, violations]) => ({
5464
+ embeddedAgentLog.warn(`codex app-server quarantined ${params.tools.length} unsupported dynamic tool ${params.tools.length === 1 ? "definition" : "definitions"}: ${params.tools.map((tool) => tool.tool).join(", ")}; retained ${params.availableToolCount} available and ${params.registeredToolCount} registered tools`, {
5465
+ tools: params.tools.map(({ tool, violations }) => ({
5457
5466
  tool,
5458
5467
  violations
5459
5468
  })),
5460
5469
  availableToolCount: params.availableToolCount,
5461
5470
  registeredToolCount: params.registeredToolCount
5462
5471
  });
5463
- }
5464
- function emitQuarantinedDynamicToolDiagnostics(tools, ctx) {
5465
- for (const tool of tools) emitTrustedDiagnosticEvent({
5472
+ for (const tool of params.tools) emitTrustedDiagnosticEvent({
5466
5473
  type: "tool.execution.blocked",
5467
- agentId: ctx?.agentId,
5468
- runId: ctx?.runId,
5469
- sessionId: ctx?.sessionId,
5470
- sessionKey: ctx?.sessionKey,
5474
+ agentId: params.hookContext?.agentId,
5475
+ runId: params.hookContext?.runId,
5476
+ sessionId: params.hookContext?.sessionId,
5477
+ sessionKey: params.hookContext?.sessionKey,
5471
5478
  toolName: tool.tool,
5472
5479
  deniedReason: "unsupported_tool_schema",
5473
5480
  reason: tool.violations.join(", ")
@@ -5644,7 +5651,36 @@ function withDynamicToolAsyncStarted(response, asyncStarted) {
5644
5651
  function normalizeToolResultMaxChars(maxChars) {
5645
5652
  return typeof maxChars === "number" && Number.isFinite(maxChars) && maxChars > 0 ? Math.floor(maxChars) : DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS;
5646
5653
  }
5647
- function convertToolContents(content, toolResultMaxChars = DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS) {
5654
+ function sanitizeToolTextRuns(rawContent) {
5655
+ const content = [];
5656
+ for (let index = 0; index < rawContent.length;) {
5657
+ const item = rawContent[index];
5658
+ if (item.type !== "text") {
5659
+ content.push(item);
5660
+ index += 1;
5661
+ continue;
5662
+ }
5663
+ const textRun = [];
5664
+ while (index < rawContent.length) {
5665
+ const next = rawContent[index];
5666
+ if (next.type !== "text") break;
5667
+ textRun.push(next);
5668
+ index += 1;
5669
+ }
5670
+ const sanitizedText = sanitizeToolResult(textRun.map((entry) => entry.text).join(""));
5671
+ let offset = 0;
5672
+ content.push(...textRun.map((entry, runIndex) => {
5673
+ const targetEnd = runIndex === textRun.length - 1 ? sanitizedText.length : Math.min(sanitizedText.length, offset + entry.text.length);
5674
+ const text = sliceUtf16Safe(sanitizedText, offset, targetEnd);
5675
+ const sanitized = Object.assign({}, entry, { text });
5676
+ offset += text.length;
5677
+ return sanitized;
5678
+ }));
5679
+ }
5680
+ return content;
5681
+ }
5682
+ function convertToolContents(rawContent, toolResultMaxChars = DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS) {
5683
+ const content = sanitizeToolTextRuns(rawContent);
5648
5684
  const maxChars = normalizeToolResultMaxChars(toolResultMaxChars);
5649
5685
  const totalTextChars = content.reduce((total, item) => total + (item.type === "text" ? item.text.length : 0), 0);
5650
5686
  if (content.reduce((total, item) => total + (item.type === "text" ? estimateToolResultTextChars(item.text) : 0), 0) <= maxChars) return content.flatMap(convertToolContent);
@@ -5758,4 +5794,4 @@ function isCronAddAction(args) {
5758
5794
  return typeof action === "string" && action.trim().toLowerCase() === "add";
5759
5795
  }
5760
5796
  //#endregion
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 };
5797
+ export { shouldRequireCodexSandboxExecServerEnvironment as C, shouldEnableCodexAppServerNativeToolSurface as S, filterCodexVisionTools as T, resolveCodexAppServerHookChannelId as _, emitDynamicToolStartedDiagnostic as a, resolveCodexNodePlacementToolConstructionPlan as b, ensureCodexSandboxExecServerEnvironment as c, CodexNativeToolLifecycleProjector as d, buildDynamicTools as f, resolveCodexAppServerExecutionCwd as g, formatCodexDynamicToolBuildStageSummary as h, emitDynamicToolErrorDiagnostic as i, releaseCodexSandboxExecServerEnvironment as l, disableCodexPluginThreadConfig as m, projectCodexExecutableDynamicTools as n, emitDynamicToolTerminalDiagnostic as o, createCodexDynamicToolBuildStageTracker as p, routeCodexAppServerElicitationRequest as r, handleCodexAppServerApprovalRequest as s, createCodexDynamicToolBridge as t, readBoundedCodexRemoteWorkspaceFile as u, resolveCodexExternalSandboxPolicyForOpenClawSandbox as v, shouldWarnCodexDynamicToolBuildStageSummary as w, resolveCodexSandboxEnvironmentSelection as x, resolveCodexMessageToolProvider as y };