@openclaw/codex 2026.9.5 → 2026.9.6

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 (120) hide show
  1. package/dist/.setup/{account-usage-runtime-C2MOIvfq.mjs → account-usage-runtime-Eem_WGqY.mjs} +5 -5
  2. package/dist/.setup/{apply-CJ0ZfmBG.mjs → apply-DDOi8uNx.mjs} +22 -28
  3. package/dist/.setup/{attempt-client-cleanup-BpYKdTA0.mjs → attempt-client-cleanup-CEv1cKwF.mjs} +58 -43
  4. package/dist/.setup/attempt-workspace-context-DUVEn_CS.mjs +331 -0
  5. package/dist/.setup/{auth-cache-key-QirmgiCo.mjs → auth-cache-key-B2mpaI9D.mjs} +9 -81
  6. package/dist/.setup/auth-profile-health-oHizqgye.mjs +53 -0
  7. package/dist/.setup/auth-profile-recovery-BREezV0L.mjs +14 -0
  8. package/dist/.setup/{binding-connection-Bm38niE8.mjs → binding-connection-ThoatDcb.mjs} +1 -1
  9. package/dist/.setup/{binding-connection.runtime-CnRMC0ta.mjs → binding-connection.runtime-yoHrABt-.mjs} +4 -4
  10. package/dist/.setup/{bounded-turn-BtlZcivC.mjs → bounded-turn-B2HScEgT.mjs} +138 -32
  11. package/dist/.setup/{build-state-Xx9ybZqa.mjs → build-state-C7EnDVgr.mjs} +1 -1
  12. package/dist/.setup/{capabilities-_vXcZGSx.mjs → capabilities-CDXOOFdZ.mjs} +1 -1
  13. package/dist/.setup/{client-vXs2929Q.mjs → client-Cs08OXVQ.mjs} +583 -559
  14. package/dist/.setup/client-catalog-response-BMgt_4q4.mjs +285 -0
  15. package/dist/.setup/{command-dispatch-naHIZwwF.mjs → command-dispatch-B4JuOsOk.mjs} +8 -4
  16. package/dist/.setup/{command-formatters-CGEkdawF.mjs → command-formatters-Bmqvr9tO.mjs} +8 -4
  17. package/dist/.setup/{command-handlers-BtAJEW1X.mjs → command-handlers-DW7Lx9oH.mjs} +106 -172
  18. package/dist/.setup/{command-rpc-DHFpn4le.mjs → command-rpc-DixIj3HW.mjs} +36 -21
  19. package/dist/.setup/{compact-C3i3qlzj.mjs → compact-Du5UvtxA.mjs} +14 -21
  20. package/dist/.setup/{config-CedDWjM-.mjs → config-BoTP_mrL.mjs} +3 -3
  21. package/dist/.setup/{config-options-BsanJ8QG.mjs → config-options-BvaRs51b.mjs} +46 -30
  22. package/dist/.setup/{config-runtime-CeAhl_h4.mjs → config-runtime-C8Rw1m1m.mjs} +1 -1
  23. package/dist/.setup/{config-security-BRQ2mgW9.mjs → config-security-BEReZ6go.mjs} +35 -120
  24. package/dist/.setup/{conversation-binding-DhzrpTM6.mjs → conversation-binding-DP-gMrBP.mjs} +21 -32
  25. package/dist/.setup/{conversation-control-DN53ns41.mjs → conversation-control-uK9Jomlc.mjs} +28 -67
  26. package/dist/.setup/{dynamic-tools-D16lOp67.mjs → dynamic-tools-BDXl-8Cw.mjs} +848 -578
  27. package/dist/.setup/{effective-mcp-catalog-K6kLCN8X.mjs → effective-mcp-catalog-CvnvnGIe.mjs} +3 -3
  28. package/dist/.setup/{ephemeral-turn-R1gA8Sn6.mjs → ephemeral-turn-poidXgek.mjs} +9 -6
  29. package/dist/.setup/{event-projector-assistant-message-DNUR1s0A.mjs → event-projector-assistant-message-BsruvNrH.mjs} +2 -4
  30. package/dist/.setup/{event-projector-items-DyTQ_XqL.mjs → event-projector-items-C6L_5ZiT.mjs} +42 -8
  31. package/dist/.setup/{event-projector-usage-C7Ntzs-f.mjs → event-projector-usage-CnBS8Wa3.mjs} +1 -1
  32. package/dist/.setup/{harness-rxaaoN-o.mjs → harness-a_MsE4-_.mjs} +23 -17
  33. package/dist/.setup/{inference-context-fulhZuz8.mjs → inference-context-D96l9-ew.mjs} +1 -1
  34. package/dist/.setup/inference-proxy-b-aJMSxm.mjs +601 -0
  35. package/dist/.setup/{isolated-completion-B6srt48J.mjs → isolated-completion-BIZFhTNG.mjs} +17 -10
  36. package/dist/.setup/launch-args-DbFCehO7.mjs +196 -0
  37. package/dist/.setup/{managed-binary-DP6Rib3P.mjs → managed-binary-BnshlFag.mjs} +13 -2
  38. package/dist/.setup/{model-catalog-DkvUuBey.mjs → model-catalog-CfXJf9Ay.mjs} +9 -10
  39. package/dist/.setup/{models-BDFgm_G-.mjs → models-KnqBv0hE.mjs} +3 -3
  40. package/dist/.setup/{native-auth-BLZGqFjw.mjs → native-auth-rWbGbMn3.mjs} +4 -4
  41. package/dist/.setup/{native-subagent-history-YXaL6dMd.mjs → native-subagent-history-0nYYgo0k.mjs} +26 -13
  42. package/dist/.setup/{native-subagent-monitor-B1hxzIOy.mjs → native-subagent-monitor-D3filLY2.mjs} +145 -317
  43. package/dist/.setup/{native-subagent-task-ids-DsuOonKv.mjs → native-subagent-task-ids-wb8CKZGN.mjs} +69 -39
  44. package/dist/.setup/{node-cli-sessions-Cl8b2ZF3.mjs → node-cli-sessions-r3avj02l.mjs} +86 -68
  45. package/dist/.setup/node-exec-server.runtime-BDwWXVpK.mjs +247 -0
  46. package/dist/.setup/{plan-xD7WKYTM.mjs → plan-CN3VwfNU.mjs} +74 -76
  47. package/dist/.setup/{plugin-activation-r8nq5Jzp.mjs → plugin-activation-DT3MNtqj.mjs} +7 -16
  48. package/dist/.setup/{plugin-app-cache-key-BOxKP7db.mjs → plugin-app-cache-key-B2CsSdnV.mjs} +4 -20
  49. package/dist/.setup/{plugin-approval-roundtrip-CjfzafAZ.mjs → plugin-approval-roundtrip-C0MZzCuP.mjs} +1 -1
  50. package/dist/.setup/{plugin-inventory-DUle8DLz.mjs → plugin-inventory-BoRei8Z4.mjs} +16 -7
  51. package/dist/.setup/{plugin-marketplace-discovery-P-Vndfqq.mjs → plugin-marketplace-discovery-CDOi8IFs.mjs} +4 -1
  52. package/dist/.setup/{plugin-metadata-cache-DsS5buNa.mjs → plugin-metadata-cache-Ctmo83k_.mjs} +224 -154
  53. package/dist/.setup/protocol-CANUwXJ3.mjs +17 -0
  54. package/dist/.setup/{provider-fFh_-VR3.mjs → provider-BOsxQHOM.mjs} +9 -5
  55. package/dist/.setup/{provider-capabilities-B8J4Dy-O.mjs → provider-capabilities-BfE88FZ5.mjs} +31 -3
  56. package/dist/.setup/{rate-limits-DOEGYRNs.mjs → rate-limits-CMU3DUD0.mjs} +116 -137
  57. package/dist/.setup/{request-BioXJLYa.mjs → request-D2L0zMrq.mjs} +23 -15
  58. package/dist/.setup/{run-attempt-rlA07pUv.mjs → run-attempt-BHu11r7h.mjs} +1197 -617
  59. package/dist/.setup/{runtime-artifact-Dy38Z5iU.mjs → runtime-artifact-CLZ8udh-.mjs} +29 -45
  60. package/dist/.setup/{sandbox-guard-BBP-RbTf.mjs → sandbox-guard-C2sRMOEY.mjs} +1 -1
  61. package/dist/.setup/{session-binding-_0W_fHRr.mjs → session-binding-Cm0apEbd.mjs} +3 -32
  62. package/dist/.setup/{session-binding-record-TH3xA0zj.mjs → session-binding-record-BGoz8wOK.mjs} +22 -3
  63. package/dist/.setup/{session-binding-sidecars-CKYEcfNn.mjs → session-binding-sidecars-BlB5EvIS.mjs} +48 -45
  64. package/dist/.setup/{session-catalog-fUdWDx-U.mjs → session-catalog-Pno1xGcl.mjs} +482 -182
  65. package/dist/.setup/{session-catalog-adoption-CR_whkEW.mjs → session-catalog-adoption-BI6UTDZT.mjs} +57 -25
  66. package/dist/.setup/{session-catalog-archive-CwZaPacf.mjs → session-catalog-archive-lHBqzhaO.mjs} +6 -6
  67. package/dist/.setup/{session-catalog-events-jMcYfQyd.mjs → session-catalog-events-Bj6j94E_.mjs} +122 -25
  68. package/dist/.setup/{session-catalog-index-B141u15s.mjs → session-catalog-index-B9_VQIUz.mjs} +178 -78
  69. package/dist/.setup/{session-catalog-index-order-Cc5BzlQr.mjs → session-catalog-index-order-hcdz07yN.mjs} +2 -2
  70. package/dist/.setup/{session-catalog-index-state-BAuDztHN.mjs → session-catalog-index-state-Dgh8ORJm.mjs} +5 -2
  71. package/dist/.setup/{session-catalog-native-projection-ChV3cCtm.mjs → session-catalog-native-projection-DowriLid.mjs} +30 -13
  72. package/dist/.setup/{session-catalog-node-adoption-BVRsYOSh.mjs → session-catalog-node-adoption-ITddWx_X.mjs} +24 -12
  73. package/dist/.setup/{session-catalog-node-continue-Dowv8osw.mjs → session-catalog-node-continue-6g6qi4A9.mjs} +22 -13
  74. package/dist/.setup/{session-catalog-projection-C1Bj9Jl-.mjs → session-catalog-projection-zK_PUsiE.mjs} +13 -5
  75. package/dist/.setup/{session-cli-DIj-IE-E.mjs → session-cli-irrD-uo7.mjs} +4 -4
  76. package/dist/.setup/{session-history-C--SrLI8.mjs → session-history-BrqhSCf9.mjs} +17 -22
  77. package/dist/.setup/{session-history-import-JHXT5Xfi.mjs → session-history-import-D-3wAgOP.mjs} +4 -4
  78. package/dist/.setup/{session-history.worker-CpNj7H_P.mjs → session-history.worker-CaCh0xF_.mjs} +5 -18
  79. package/dist/.setup/{session-initialization-DH1z6fXn.mjs → session-initialization-rcgbZiid.mjs} +2 -2
  80. package/dist/.setup/{session-permission-policy-DhurXY0l.mjs → session-permission-policy-BI1QK_98.mjs} +2 -2
  81. package/dist/.setup/{session-retirement-CU87AOg9.mjs → session-retirement-BH-S6qkJ.mjs} +25 -6
  82. package/dist/.setup/{settled-turn-finalizer-CiY2bLFK.mjs → settled-turn-finalizer-DiAi6Kx6.mjs} +8 -8
  83. package/dist/.setup/{shared-client-BpTtuLQ5.mjs → shared-client-DA4VR4Eb.mjs} +59 -89
  84. package/dist/.setup/{side-question-DSCZyoBD.mjs → side-question-BJ1wSDcB.mjs} +147 -211
  85. package/dist/.setup/{source-Bjp4UQij.mjs → source-CUzJq5WN.mjs} +9 -9
  86. package/dist/.setup/spawn-error-CL3n6MAa.mjs +57 -0
  87. package/dist/.setup/{thread-archive-guard-jmecf6OJ.mjs → thread-archive-guard-C9frICzX.mjs} +10 -3
  88. package/dist/.setup/{thread-history-page-BTx-4xsg.mjs → thread-history-page-DoVpnHim.mjs} +1 -1
  89. package/dist/.setup/{thread-lifecycle-Cik28qWn.mjs → thread-lifecycle-B3mfz1TG.mjs} +172 -2271
  90. package/dist/.setup/{thread-ownership-skomxnIt.mjs → thread-ownership-DcTtAcXj.mjs} +4 -2
  91. package/dist/.setup/{thread-policy-Cjs4B8xf.mjs → thread-policy-DGuQsuFC.mjs} +19 -3
  92. package/dist/.setup/thread-requests-BLvGkP2R.mjs +2205 -0
  93. package/dist/.setup/{thread-resume-BkNpj9Pz.mjs → thread-resume-DIBXwOpE.mjs} +6 -6
  94. package/dist/.setup/{timeout-DZF05XrR.mjs → timeout-C910MdAB.mjs} +19 -2
  95. package/dist/.setup/{transcript-history-projection-DHNBDhCU.mjs → transcript-history-projection-ZZpkFkJ_.mjs} +94 -64
  96. package/dist/.setup/{transcript-mirror-D4qp_leP.mjs → transcript-mirror-BtHDBp1G.mjs} +218 -198
  97. package/dist/.setup/{transport-stdio-Cmx94Oiy.mjs → transport-stdio-h2VHOskC.mjs} +135 -55
  98. package/dist/.setup/{upstream-session-fork-D3UKxgVD.mjs → upstream-session-fork-BUr4OUbC.mjs} +44 -36
  99. package/dist/.setup/{usage-BOgBEKso.mjs → usage-BML0r-ua.mjs} +5 -4
  100. package/dist/.setup/{usage-limit-error-C0ow7DOc.mjs → usage-limit-error-DUMbefVY.mjs} +4 -4
  101. package/dist/.setup/{web-search-provider.runtime-CTGuJt6i.mjs → web-search-provider.runtime-CDKlN-Rv.mjs} +4 -3
  102. package/dist/api.js +23 -12
  103. package/dist/catalog-page-worker-entrypoint.js +12 -0
  104. package/dist/catalog-page.worker.js +9 -0
  105. package/dist/cli-metadata.js +1 -1
  106. package/dist/doctor-contract-api.js +2 -2
  107. package/dist/harness.js +1 -1
  108. package/dist/index.js +221 -170
  109. package/dist/media-understanding-provider.js +2 -2
  110. package/dist/migration-provider-api.js +1 -1
  111. package/dist/provider-discovery.js +1 -1
  112. package/dist/session-history-worker-runtime.js +15 -32
  113. package/dist/session-history.worker.js +1 -1
  114. package/package.json +9 -6
  115. package/dist/.setup/attempt-context-VXVwTCfS.mjs +0 -636
  116. package/dist/.setup/inference-proxy-CC15HuSi.mjs +0 -311
  117. package/dist/.setup/launch-args-DnMak49W.mjs +0 -111
  118. package/dist/.setup/node-exec-server.runtime-BaViBKNz.mjs +0 -226
  119. package/dist/.setup/version-CoqzXPgK.mjs +0 -12
  120. package/dist/.setup/{native-execution-policy-YBMHSzC4.mjs → native-execution-policy-umd0tgh4.mjs} +1 -1
@@ -1,24 +1,26 @@
1
1
  import { CODEX_NATIVE_TOOL_REQUIREMENTS } from "../native-tool-policy.js";
2
- import { d as CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE, m as isJsonObject, p as flattenCodexDynamicToolFunctions } from "./config-security-BRQ2mgW9.mjs";
3
2
  import { d as readCodexPluginConfig, l as isCodexRemoteExecPlacementSandbox } from "./config-parsing-CcB9iPoq.mjs";
4
- import { B as withDynamicToolTranscriptDetails, E as createCodexElicitationResponse, L as resolveCodexToolAbortTerminalReason, R as createFailedDynamicToolResponse, w as readCodexTurn, z as withDynamicToolExecutionState } from "./client-vXs2929Q.mjs";
5
- import { o as formatCodexDisplayText } from "./command-formatters-CGEkdawF.mjs";
6
- import { c as itemName, f as shouldClearTerminalPresentationForNativeItem, l as itemStatus, n as auditNativeToolTerminalStatus, r as auditNativeToolUnfinishedStatus, t as auditNativeToolName, x as readItem } from "./event-projector-items-DyTQ_XqL.mjs";
7
- import { s as codexAppIdentityKey } from "./plugin-app-cache-key-BOxKP7db.mjs";
8
- import "./config-CedDWjM-.mjs";
3
+ import { i as isJsonObject, r as flattenCodexDynamicToolFunctions, t as CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE } from "./protocol-CANUwXJ3.mjs";
4
+ import { o as formatCodexDisplayText } from "./command-formatters-Bmqvr9tO.mjs";
5
+ import { S as readItem, c as itemName, f as shouldClearTerminalPresentationForNativeItem, l as itemStatus, n as auditNativeToolTerminalStatus, r as auditNativeToolUnfinishedStatus, t as auditNativeToolName } from "./event-projector-items-C6L_5ZiT.mjs";
6
+ import { s as codexAppIdentityKey } from "./plugin-app-cache-key-B2CsSdnV.mjs";
7
+ import "./config-BoTP_mrL.mjs";
9
8
  import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-tupAr-2o.mjs";
10
- import { v as isCodexNotificationForTurn } from "./attempt-client-cleanup-BpYKdTA0.mjs";
11
- import { H as emitCodexNativePreToolUseFailureDiagnostic, at as filterCodexDynamicToolsForDisabledNativeSurface, ct as isSystemAgentOnlyCodexDynamicToolAllowlist, it as filterCodexDynamicTools, lt as normalizeCodexDynamicToolName, ot as isForcedPrivateQaCodexRuntime, w as resolveCodexWebSearchPlan } from "./thread-lifecycle-Cik28qWn.mjs";
12
- import { n as resolveCodexNativeExecutionPolicy, r as resolveCodexNodeExecToolOverrides } from "./native-execution-policy-YBMHSzC4.mjs";
13
- 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-B8J4Dy-O.mjs";
14
- 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-CjfzafAZ.mjs";
9
+ import { V as protectCodexAppServerLiveThread, v as retainSharedCodexAppServerClientIfCurrent } from "./shared-client-DA4VR4Eb.mjs";
10
+ import { E as createCodexElicitationResponse, L as resolveCodexToolAbortTerminalReason, R as createFailedDynamicToolResponse, T as readCodexTurnCompletedNotification, m as CodexServerRequestResolvedError, z as failedToolResult } from "./client-Cs08OXVQ.mjs";
11
+ import { b as readCodexNotificationTurnId, v as isCodexNotificationForTurn, y as readCodexNotificationThreadId } from "./attempt-client-cleanup-CEv1cKwF.mjs";
12
+ import { A as emitCodexNativePreToolUseFailureDiagnostic, at as filterCodexDynamicTools, lt as isSystemAgentOnlyCodexDynamicToolAllowlist, ot as filterCodexDynamicToolsForDisabledNativeSurface, p as resolveCodexWebSearchPlan, st as isForcedPrivateQaCodexRuntime, ut as normalizeCodexDynamicToolName } from "./thread-requests-BLvGkP2R.mjs";
13
+ import { n as resolveCodexNativeExecutionPolicy, r as resolveCodexNodeExecToolOverrides } from "./native-execution-policy-umd0tgh4.mjs";
14
+ import { a as CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME, c as createNodeExecAliasDynamicTool, d as isCodexDynamicToolExcluded, i as CODEX_GATEWAY_PROCESS_DYNAMIC_TOOL_NAME, l as createSandboxExecProjection, o as createGatewayExecProjection, r as CODEX_GATEWAY_EXEC_DYNAMIC_TOOL_NAME, s as createGatewayProcessProjection, u as createSandboxProcessProjection } from "./provider-capabilities-BfE88FZ5.mjs";
15
+ 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-C0MZzCuP.mjs";
15
16
  import { createRequire } from "node:module";
16
- import { asNonArrayRecord, asOptionalRecord, isRecord, normalizeOptionalString, normalizeTrimmedStringList, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
17
+ import { asNonArrayRecord, asOptionalRecord, isRecord, normalizeTrimmedStringList, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
17
18
  import { createHash, randomUUID } from "node:crypto";
18
19
  import { asDateTimestampMs } from "openclaw/plugin-sdk/number-runtime";
19
- import { killProcessTree } from "openclaw/plugin-sdk/process-runtime";
20
+ import { signalProcessTree, spawnTerminalPty } from "openclaw/plugin-sdk/process-runtime";
20
21
  import { validateJsonSchemaValue } from "openclaw/plugin-sdk/json-schema-runtime";
21
22
  import path, { posix } from "node:path";
23
+ import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
22
24
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
23
25
  import { once } from "node:events";
24
26
  import { spawn } from "node:child_process";
@@ -27,24 +29,19 @@ import { DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS, estimateToolResultTextChars, resolv
27
29
  import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
28
30
  import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
29
31
  import { createStageTimingTracker, formatStageTimings } from "openclaw/plugin-sdk/time-runtime";
30
- import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
31
32
  import { fileURLToPath, pathToFileURL } from "node:url";
32
33
  import { resolveAgentDir } from "openclaw/plugin-sdk/agent-runtime";
33
- import { HEARTBEAT_RESPONSE_TOOL_NAME, applyEmbeddedAttemptToolsAllow, 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";
34
+ import { HEARTBEAT_RESPONSE_TOOL_NAME, applyEmbeddedAttemptToolsAllow, buildAgentHookContextChannelFields, buildEmbeddedAttemptToolRunContext, consumeAdjustedParamsForToolCall, consumePreExecutionBlockedToolCall, createAgentToolResultMiddlewareRunner, createCodexAppServerToolResultExtensionRunner, embeddedAgentLog, extractMessagingToolSend, extractMessagingToolSendResult, extractMessagingToolSourceReplyPayload, extractToolResultMediaArtifact, filterProviderNormalizableTools, filterToolResultMediaUrls, finalizeAgentToolAvailability, finalizeToolTerminalPresentation, formatApprovalDisplayPath, formatToolExecutionErrorMessage, getBeforeToolCallFailureDisposition, getChannelAgentToolMeta, getPluginToolMeta, getPluginToolSideEffectOwnerKey, hasNativeHookRelayInvocation, invokeNativeHookRelay, isDeliveredMessageToolOnlySourceReplyResult, isDeliveredMessagingToolResult, isDeliveredMessagingToolSendToCurrentSource, isHostScopedAgentToolActive, isMessagingTool, isReplaySafeToolCall, isSubagentSessionKey, isToolResultError, isToolWrappedWithBeforeToolCallHook, normalizeAgentRuntimeTools, normalizeHeartbeatToolResponse, projectPluginMessageDeliveryFact, projectRuntimeToolInputSchema, readEmbeddedMessageDeliveryFact, readToolOperatorHint, resolveAttemptSpawnWorkspaceDir, resolveModelAuthMode, resolveNativeHookRelayDeferredToolApproval, resolveToolExecutionErrorKind, resolveToolResultFailureKind, runAgentHarnessAfterToolCallHook, sanitizeToolResult, setBeforeToolCallDiagnosticsEnabled, supportsModelTools, wrapToolWithBeforeToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
34
35
  import { formatErrorMessage as formatErrorMessage$2, redactSensitiveText } from "openclaw/plugin-sdk/security-runtime";
35
- import { consumeTrustedToolNoStartError, copyInternalToolResultState, getCoreTtsToolResultMediaUrls } from "openclaw/plugin-sdk/agent-harness-tool-runtime";
36
+ import { consumeTrustedToolNoStartError, copyInternalToolResultState, getCoreTtsToolResultMediaUrls, normalizeAcceptedSessionSpawnResult } from "openclaw/plugin-sdk/agent-harness-tool-runtime";
36
37
  import { isIP } from "node:net";
37
38
  import { redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
38
39
  import { formatMcpCodexApprovalRemedy, requiresMcpCodexToolApproval, resolveCodexScheduledToolProjectionFactory, resolveProjectedMcpCodexToolApprovalMode, runWithCronCreatorAuthorityCapabilityResolver } from "openclaw/plugin-sdk/codex-mcp-projection";
39
- import { buildRemoteCommand, isToolAllowed, sanitizeEnvVars } from "openclaw/plugin-sdk/sandbox";
40
+ import { SANDBOX_COMMAND_MAX_BUFFER_BYTES, buildRemoteCommand, isToolAllowed, prepareSandboxProcessCleanup, sanitizeEnvVars } from "openclaw/plugin-sdk/sandbox";
40
41
  import { SsrFBlockedError, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
41
- import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
42
+ import { normalizeMediaReferenceForComparison, saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
42
43
  import { getMediaDir } from "openclaw/plugin-sdk/media-runtime";
43
44
  import { normalizeOpenAIStrictCompatSchema } from "openclaw/plugin-sdk/provider-tools";
44
- //#region extensions/codex/src/app-server/dynamic-tool-build-state.ts
45
- /** Mutable dependency seam shared by dynamic-tool construction and its behavioral tests. */
46
- const dynamicToolBuildState = {};
47
- //#endregion
48
45
  //#region extensions/codex/src/app-server/remote-workspace-path.ts
49
46
  /** Projects a gateway workspace path into the remote Codex execution workspace. */
50
47
  function mapCodexAppServerRemoteWorkspacePath(params) {
@@ -193,7 +190,6 @@ async function buildDynamicTools(input) {
193
190
  const toolBuildStages = createCodexDynamicToolBuildStageTracker();
194
191
  const modelHasVision = params.model.input?.includes("image") ?? false;
195
192
  const agentDir = params.agentDir ?? resolveAgentDir(params.config ?? {}, input.sessionAgentId);
196
- const injectedOpenClawCodingToolsFactory = dynamicToolBuildState.openClawCodingToolsFactory;
197
193
  const nativeExecutionPolicy = resolveCodexNativeExecutionPolicyForRun(params, {
198
194
  agentId: input.policyAgentId,
199
195
  runtimeSessionKey: input.sandboxSessionKey,
@@ -249,7 +245,6 @@ async function buildDynamicTools(input) {
249
245
  config: params.config,
250
246
  skillsSnapshot: params.skillsSnapshot,
251
247
  ...params.skillLibraryAuthoring ? { skillWorkshop: { libraryAuthoring: params.skillLibraryAuthoring } } : {},
252
- githubPublicationAvailable: params.githubPublicationAvailable,
253
248
  authProfileStore: params.toolAuthProfileStore ?? params.authProfileStore,
254
249
  abortSignal: input.runAbortController.signal,
255
250
  emitBeforeToolCallDiagnostics: false,
@@ -296,7 +291,6 @@ async function buildDynamicTools(input) {
296
291
  input.onMessageToolTargetResolved?.(options.requireExplicitMessageTarget === true);
297
292
  const buildOpenClawCodingTools = () => {
298
293
  const bindingOptions = { cwd: input.effectiveCwd ?? input.effectiveWorkspace };
299
- if (injectedOpenClawCodingToolsFactory) return params.hostCapabilities.bindToolSurface(injectedOpenClawCodingToolsFactory(options), bindingOptions);
300
294
  const createToolSurface = params.hostCapabilities.createToolSurface;
301
295
  if (!createToolSurface) throw new Error("Codex tool construction requires a current host capability");
302
296
  return createToolSurface(options, bindingOptions);
@@ -509,29 +503,9 @@ function addSandboxShellDynamicToolsIfAvailable(filteredTools, allTools, input,
509
503
  if (isCodexMemoryFlushRun(input.params) || !executionPolicy.nativeToolSurfaceAllowed || !input.sandbox?.enabled || !input.sandbox.backendId.trim() || input.nativeToolSurfaceEnabled !== false || isSandboxShellDynamicToolExcluded(input.pluginConfig)) return filteredTools;
510
504
  const execTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "exec");
511
505
  const processTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "process");
512
- if (!execTool || !processTool) return filteredTools;
513
- const sandboxExecTool = {
514
- ...execTool,
515
- name: "sandbox_exec",
516
- 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.",
517
- execute: async (toolCallId, args, signal, onUpdate) => {
518
- const result = await execTool.execute(toolCallId, args, signal, onUpdate);
519
- return {
520
- ...result,
521
- 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)
522
- };
523
- }
524
- };
525
- const sandboxProcessTool = {
526
- ...processTool,
527
- name: "sandbox_process",
528
- 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."
529
- };
530
- return [
531
- ...filteredTools,
532
- sandboxExecTool,
533
- sandboxProcessTool
534
- ];
506
+ if (!execTool) return filteredTools;
507
+ const sandboxTools = [...filteredTools, createSandboxExecProjection(execTool)];
508
+ return processTool ? [...sandboxTools, createSandboxProcessProjection(processTool)] : sandboxTools;
535
509
  }
536
510
  function isSandboxShellDynamicToolExcluded(config) {
537
511
  return isCodexDynamicToolExcluded(config, [
@@ -640,17 +614,17 @@ var CodexNativeToolLifecycleProjector = class {
640
614
  const params = isJsonObject(notification.params) ? notification.params : void 0;
641
615
  if (!params || !isCodexNotificationForTurn(params, this.threadId, this.turnId)) return;
642
616
  if (isMcpToolCallItemNotification(notification.method, params)) this.pendingMcpNotifications += 1;
643
- else if (notification.method === "turn/completed" && readCodexTurn(params.turn)?.id === this.turnId) this.turnCompleted = true;
617
+ else if (notification.method === "turn/completed" && readCodexTurnCompletedNotification(params)?.turn.id === this.turnId) this.turnCompleted = true;
644
618
  }
645
619
  handleNotification(notification) {
646
620
  const params = isJsonObject(notification.params) ? notification.params : void 0;
647
621
  if (!params || !isCodexNotificationForTurn(params, this.threadId, this.turnId)) return;
648
622
  if (this.pendingMcpNotifications > 0 && isMcpToolCallItemNotification(notification.method, params)) this.pendingMcpNotifications -= 1;
649
623
  if (notification.method === "turn/completed") {
650
- const turn = readCodexTurn(params.turn);
624
+ const turn = readCodexTurnCompletedNotification(params)?.turn;
651
625
  if (!turn || turn.id !== this.turnId) return;
652
626
  this.turnCompleted = true;
653
- for (const item of turn.items ?? []) this.recordSnapshotItem(item);
627
+ for (const item of turn.items) this.recordSnapshotItem(item);
654
628
  return;
655
629
  }
656
630
  if (notification.method === "rawResponseItem/completed") {
@@ -894,6 +868,22 @@ const ATTACHMENT_MEDIA_KEYS = [
894
868
  "fileUrl",
895
869
  "url"
896
870
  ];
871
+ function collectCodexMessageMediaUrls(record) {
872
+ const urls = [];
873
+ const push = (value) => {
874
+ if (typeof value === "string" && value.trim()) urls.push(value.trim());
875
+ };
876
+ for (const key of MESSAGE_MEDIA_KEYS) push(record[key]);
877
+ for (const key of MESSAGE_MEDIA_ARRAY_KEYS) {
878
+ const value = record[key];
879
+ if (Array.isArray(value)) for (const entry of value) push(entry);
880
+ }
881
+ if (Array.isArray(record.attachments)) for (const attachment of record.attachments) {
882
+ if (!isRecord(attachment)) continue;
883
+ for (const key of ATTACHMENT_MEDIA_KEYS) push(attachment[key]);
884
+ }
885
+ return urls;
886
+ }
897
887
  /** Reads actual remote bytes with a cap enforced by Codex before transport. */
898
888
  async function readBoundedCodexRemoteWorkspaceFile(params) {
899
889
  if (!Number.isSafeInteger(params.maxBytes) || params.maxBytes < 0) throw new Error("Codex remote workspace upload requires a valid media byte limit.");
@@ -902,10 +892,10 @@ async function readBoundedCodexRemoteWorkspaceFile(params) {
902
892
  let offset = 0;
903
893
  let expectedSize;
904
894
  let expectedRevision;
905
- const startedAt = Date.now();
895
+ const startedAt = performance.now();
906
896
  do {
907
897
  params.signal?.throwIfAborted();
908
- const timeoutMs = params.timeoutMs === void 0 ? void 0 : params.timeoutMs - (Date.now() - startedAt);
898
+ const timeoutMs = params.timeoutMs === void 0 ? void 0 : Math.floor(params.timeoutMs - (performance.now() - startedAt));
909
899
  if (timeoutMs !== void 0 && timeoutMs <= 0) throw new Error("Codex remote workspace file transfer timed out.");
910
900
  let response;
911
901
  try {
@@ -966,7 +956,11 @@ async function readBoundedCodexRemoteWorkspaceFile(params) {
966
956
  /** Stages authoritative bounded remote bytes into immutable Gateway-owned media. */
967
957
  async function prepareCodexRemoteWorkspaceMessageMedia(params) {
968
958
  const { localWorkspaceRoot, remoteWorkspaceRoot } = params;
969
- if (!localWorkspaceRoot || !remoteWorkspaceRoot) return params.args;
959
+ const sourcePathsByStagedPath = /* @__PURE__ */ new Map();
960
+ if (!localWorkspaceRoot || !remoteWorkspaceRoot) return {
961
+ args: params.args,
962
+ sourcePathsByStagedPath
963
+ };
970
964
  const remotePathsByLocalPath = /* @__PURE__ */ new Map();
971
965
  const gatewayManagedPaths = /* @__PURE__ */ new Set();
972
966
  const gatewayMediaRoot = getMediaDir();
@@ -985,11 +979,18 @@ async function prepareCodexRemoteWorkspaceMessageMedia(params) {
985
979
  });
986
980
  if (value.trim() && !isCodexPassThroughMediaSource(value)) {
987
981
  attachmentEntries += 1;
988
- remotePathsByLocalPath.set(mapped, mapCodexAppServerRemoteWorkspacePath({
982
+ const remotePath = mapCodexAppServerRemoteWorkspacePath({
989
983
  value: mapped,
990
984
  localWorkspaceRoot,
991
985
  remoteWorkspaceRoot
992
- }));
986
+ });
987
+ const sourcePaths = remotePathsByLocalPath.get(mapped)?.sourcePaths ?? /* @__PURE__ */ new Set();
988
+ sourcePaths.add(value);
989
+ sourcePaths.add(remotePath);
990
+ remotePathsByLocalPath.set(mapped, {
991
+ remotePath,
992
+ sourcePaths
993
+ });
993
994
  }
994
995
  return mapped;
995
996
  };
@@ -1026,18 +1027,21 @@ async function prepareCodexRemoteWorkspaceMessageMedia(params) {
1026
1027
  }
1027
1028
  if (attachmentEntries > REMOTE_WORKSPACE_MEDIA_MAX_ATTACHMENTS) throw new Error(`Codex remote workspace upload exceeds the ${REMOTE_WORKSPACE_MEDIA_MAX_ATTACHMENTS}-attachment limit.`);
1028
1029
  for (const managedPath of gatewayManagedPaths) await assertGatewayManagedMediaPath(managedPath, gatewayMediaRoot);
1029
- if (remotePathsByLocalPath.size === 0) return mappedArgs;
1030
+ if (remotePathsByLocalPath.size === 0) return {
1031
+ args: mappedArgs,
1032
+ sourcePathsByStagedPath
1033
+ };
1030
1034
  const readRemoteFile = params.readRemoteFile;
1031
1035
  if (!readRemoteFile) throw new Error("Codex remote workspace file transfer requires an active app-server client.");
1032
1036
  const maxBytes = params.maxBytes ?? REMOTE_WORKSPACE_MEDIA_MAX_BYTES;
1033
1037
  const timeoutMs = params.timeoutMs ?? REMOTE_WORKSPACE_MEDIA_TIMEOUT_MS;
1034
- const deadline = Date.now() + timeoutMs;
1038
+ const deadline = performance.now() + timeoutMs;
1035
1039
  const stagedPaths = /* @__PURE__ */ new Map();
1036
1040
  let totalBytes = 0;
1037
- for (const [localPath, remotePath] of remotePathsByLocalPath) {
1041
+ for (const [localPath, { remotePath, sourcePaths }] of remotePathsByLocalPath) {
1038
1042
  params.signal?.throwIfAborted();
1039
1043
  const remainingBytes = maxBytes - totalBytes;
1040
- const remainingMs = deadline - Date.now();
1044
+ const remainingMs = Math.floor(deadline - performance.now());
1041
1045
  if (remainingMs <= 0) throw new Error("Codex remote workspace attachment batch timed out.");
1042
1046
  const response = await readRemoteFile({
1043
1047
  path: remotePath,
@@ -1053,8 +1057,15 @@ async function prepareCodexRemoteWorkspaceMessageMedia(params) {
1053
1057
  totalBytes += remoteBuffer.byteLength;
1054
1058
  const saved = await saveMediaBuffer(remoteBuffer, void 0, "outbound", maxBytes, path.basename(remotePath));
1055
1059
  stagedPaths.set(localPath, saved.path);
1060
+ sourcePathsByStagedPath.set(normalizeMediaReferenceForComparison(saved.path), [...sourcePaths]);
1056
1061
  }
1057
- return mapMessageMediaValues(mappedArgs, (value) => stagedPaths.get(value) ?? value);
1062
+ return {
1063
+ args: mapMessageMediaValues(mappedArgs, (value) => stagedPaths.get(value) ?? value),
1064
+ sourcePathsByStagedPath
1065
+ };
1066
+ }
1067
+ function resolveCodexMediaSourceUrls(mediaUrls, sourcePathsByStagedPath) {
1068
+ return [...new Set(mediaUrls.flatMap((url) => [url, ...sourcePathsByStagedPath?.get(normalizeMediaReferenceForComparison(url)) ?? []]))];
1058
1069
  }
1059
1070
  async function assertGatewayManagedMediaPath(value, mediaRoot) {
1060
1071
  const opened = await (await root(mediaRoot, { symlinks: "reject" })).open(path.relative(mediaRoot, value), { symlinks: "reject" });
@@ -1086,6 +1097,209 @@ function mapMessageMediaValues(args, mapValue) {
1086
1097
  return mapped;
1087
1098
  }
1088
1099
  //#endregion
1100
+ //#region extensions/codex/src/app-server/native-process-authority.ts
1101
+ const clients = /* @__PURE__ */ new WeakMap();
1102
+ /** A physical client joins native call receipts to its concrete sandbox processes. */
1103
+ function getCodexNativeProcessClient(client) {
1104
+ let owner = clients.get(client);
1105
+ if (!owner) {
1106
+ owner = new CodexNativeProcessClient(client);
1107
+ clients.set(client, owner);
1108
+ }
1109
+ return owner;
1110
+ }
1111
+ function hasCodexNativeBackgroundProcesses(client, threadId) {
1112
+ return clients.get(client)?.hasProcesses(threadId) ?? false;
1113
+ }
1114
+ var CodexNativeProcessClient = class {
1115
+ constructor(client) {
1116
+ this.client = client;
1117
+ this.id = randomUUID();
1118
+ this.authPath = `/openclaw-${randomUUID()}`;
1119
+ this.threads = /* @__PURE__ */ new Map();
1120
+ this.closed = false;
1121
+ client.addNotificationHandler((notification) => {
1122
+ if (!isJsonObject(notification.params)) return;
1123
+ const threadId = readCodexNotificationThreadId(notification.params);
1124
+ const turnId = readCodexNotificationTurnId(notification.params);
1125
+ const commands = threadId ? this.threads.get(threadId) : void 0;
1126
+ if (!commands || !turnId) return;
1127
+ if (notification.method === "turn/completed") {
1128
+ for (const command of commands.values()) if (command.turnId === turnId) this.closeAdmission(command);
1129
+ } else if (notification.method === "item/completed") {
1130
+ const item = notification.params.item;
1131
+ const command = isJsonObject(item) && typeof item.id === "string" ? commands.get(item.id) : void 0;
1132
+ if (command?.turnId === turnId) this.closeAdmission(command);
1133
+ }
1134
+ });
1135
+ client.addCloseHandler(() => {
1136
+ this.closed = true;
1137
+ const owners = /* @__PURE__ */ new Set();
1138
+ for (const commands of this.threads.values()) for (const command of commands.values()) owners.add(command.owner);
1139
+ for (const owner of owners) owner.cancelClient(this);
1140
+ });
1141
+ }
1142
+ admit(owner, receipt, parentTurn, assertActive) {
1143
+ if (this.closed) throw new Error("Codex process source client is closed");
1144
+ let commands = this.threads.get(receipt.threadId);
1145
+ const existing = commands?.get(receipt.itemId);
1146
+ if (existing) {
1147
+ if (existing.owner === owner && existing.turnId === receipt.turnId && existing.accepting) return;
1148
+ throw new Error("Codex reused an unsettled native command identity");
1149
+ }
1150
+ if (!commands) {
1151
+ commands = /* @__PURE__ */ new Map();
1152
+ this.threads.set(receipt.threadId, commands);
1153
+ }
1154
+ const command = {
1155
+ ...receipt,
1156
+ owner,
1157
+ client: this,
1158
+ parentTurn,
1159
+ accepting: true,
1160
+ processes: /* @__PURE__ */ new Set(),
1161
+ assertActive,
1162
+ releaseClient: retainSharedCodexAppServerClientIfCurrent(this.client),
1163
+ releaseThread: protectCodexAppServerLiveThread(this.client, receipt.threadId)
1164
+ };
1165
+ commands.set(receipt.itemId, command);
1166
+ owner.commands.add(command);
1167
+ }
1168
+ hasProcesses(threadId) {
1169
+ return [...this.threads.get(threadId)?.values() ?? []].some((command) => command.processes.size > 0);
1170
+ }
1171
+ claim(metadata, terminate) {
1172
+ if (this.closed || !isJsonObject(metadata) || typeof metadata.threadId !== "string" || typeof metadata.toolCallId !== "string") throw new Error("Codex process start requires its admitted native command");
1173
+ const command = this.threads.get(metadata.threadId)?.get(metadata.toolCallId);
1174
+ if (!command) throw new Error("Codex process start has no admitted native command");
1175
+ const assertAdmission = () => {
1176
+ command.owner.assertCurrent();
1177
+ command.assertActive();
1178
+ if (this.closed || !command.accepting || this.threads.get(command.threadId)?.get(command.itemId) !== command) throw new Error("Codex native command admission has ended");
1179
+ };
1180
+ assertAdmission();
1181
+ const process = { terminate };
1182
+ command.processes.add(process);
1183
+ let settled = false;
1184
+ return {
1185
+ assertAdmission,
1186
+ assertCurrent: () => {
1187
+ command.owner.assertCurrent();
1188
+ if (this.closed || settled) throw new Error("Codex native process authority has ended");
1189
+ },
1190
+ settle: () => {
1191
+ if (settled) return;
1192
+ settled = true;
1193
+ command.processes.delete(process);
1194
+ this.forgetSettled(command);
1195
+ },
1196
+ fail: (error) => command.owner.reportSettlementFailure(error)
1197
+ };
1198
+ }
1199
+ closeAdmission(command) {
1200
+ command.accepting = false;
1201
+ this.forgetSettled(command);
1202
+ }
1203
+ forgetSettled(command) {
1204
+ if (command.accepting || command.processes.size > 0) return;
1205
+ const commands = this.threads.get(command.threadId);
1206
+ if (commands?.get(command.itemId) === command) {
1207
+ commands.delete(command.itemId);
1208
+ if (commands.size === 0) this.threads.delete(command.threadId);
1209
+ }
1210
+ command.owner.commands.delete(command);
1211
+ command.releaseThread();
1212
+ command.releaseClient?.();
1213
+ command.owner.releaseIfSettled();
1214
+ }
1215
+ };
1216
+ /** Original-source custody outlives foreground authority, never permitting new admission after release. */
1217
+ var CodexNativeProcessAuthority = class {
1218
+ constructor(host, onCleanupFailure) {
1219
+ this.onCleanupFailure = onCleanupFailure;
1220
+ this.commands = /* @__PURE__ */ new Set();
1221
+ this.holds = 1;
1222
+ this.cancelled = false;
1223
+ this.released = false;
1224
+ this.onAbort = () => {
1225
+ if (!this.cancelled) this.cancel().catch(this.onCleanupFailure);
1226
+ };
1227
+ this.source = host.retainSourceAuthority?.();
1228
+ this.source?.signal?.addEventListener("abort", this.onAbort, { once: true });
1229
+ if (this.source?.signal?.aborted) this.onAbort();
1230
+ }
1231
+ assertCurrent() {
1232
+ if (this.cancelled || this.released) throw new Error("Codex native process source has ended");
1233
+ try {
1234
+ this.source?.assertCurrent();
1235
+ if (this.cancelled || this.released) throw new Error("Codex native process source has ended");
1236
+ } catch (error) {
1237
+ this.onAbort();
1238
+ throw error;
1239
+ }
1240
+ }
1241
+ bindTurn(client, threadId, turnId) {
1242
+ this.parentTurn = {
1243
+ client,
1244
+ threadId,
1245
+ turnId
1246
+ };
1247
+ }
1248
+ admit(client, receipt, assertAdmissionCurrent, childParentThreadId) {
1249
+ this.assertCurrent();
1250
+ if (this.holds === 0) throw new Error("Codex native process admission is closed");
1251
+ const parent = this.parentTurn;
1252
+ if (parent?.client !== client || (childParentThreadId ? childParentThreadId !== parent.threadId : receipt.threadId !== parent.threadId || receipt.turnId !== parent.turnId)) throw new Error("Codex native command does not belong to its admitted turn");
1253
+ assertAdmissionCurrent();
1254
+ getCodexNativeProcessClient(client).admit(this, receipt, parent, assertAdmissionCurrent);
1255
+ }
1256
+ retainAdmission() {
1257
+ this.assertCurrent();
1258
+ this.holds += 1;
1259
+ let held = true;
1260
+ return () => {
1261
+ if (held) {
1262
+ held = false;
1263
+ this.release();
1264
+ }
1265
+ };
1266
+ }
1267
+ release() {
1268
+ if (this.holds === 0) return;
1269
+ this.holds -= 1;
1270
+ if (this.holds === 0) for (const command of this.commands) command.client.closeAdmission(command);
1271
+ this.releaseIfSettled();
1272
+ }
1273
+ releaseIfSettled() {
1274
+ if (this.released || this.holds > 0 || this.commands.size > 0) return;
1275
+ this.released = true;
1276
+ this.source?.signal?.removeEventListener("abort", this.onAbort);
1277
+ this.source?.release();
1278
+ }
1279
+ cancelTurn(client, threadId, turnId) {
1280
+ const owner = getCodexNativeProcessClient(client);
1281
+ return this.terminate([...this.commands].filter((command) => command.client === owner && command.parentTurn.threadId === threadId && command.parentTurn.turnId === turnId));
1282
+ }
1283
+ cancelClient(client) {
1284
+ this.terminate([...this.commands].filter((command) => command.client === client)).catch(this.onCleanupFailure);
1285
+ }
1286
+ reportSettlementFailure(error) {
1287
+ this.onCleanupFailure(new AggregateError([error], "Codex native process cleanup failed; background work remains unsettled"));
1288
+ }
1289
+ cancel() {
1290
+ this.cancelled = true;
1291
+ return this.cancellation ??= this.terminate([...this.commands]);
1292
+ }
1293
+ async terminate(commands) {
1294
+ const processes = commands.flatMap((command) => {
1295
+ command.client.closeAdmission(command);
1296
+ return [...command.processes];
1297
+ });
1298
+ const failures = (await Promise.allSettled(processes.map((process) => process.terminate()))).flatMap((result) => result.status === "rejected" ? [result.reason] : []);
1299
+ if (failures.length > 0) throw new AggregateError(failures, "Codex native process cleanup failed; background work remains unsettled");
1300
+ }
1301
+ };
1302
+ //#endregion
1089
1303
  //#region extensions/codex/src/app-server/sandbox-exec-server-node-relay.ts
1090
1304
  const CODEX_NODE_EXEC_SERVER_MAX_MESSAGE_BYTES$1 = 67108864;
1091
1305
  const CODEX_NODE_EXEC_SERVER_MAX_FAILURE_DETAIL_CHARS = 240;
@@ -2064,7 +2278,7 @@ function metadataResponse(stat) {
2064
2278
  /** Owns one sandbox subprocess tree through close, reaping, and backend finalization. */
2065
2279
  const SANDBOX_CHILD_TERM_GRACE_MS = 1e3;
2066
2280
  const SANDBOX_CHILD_REAP_TIMEOUT_MS = 4500;
2067
- const SANDBOX_EXEC_MARKER = "CODEX_SANDBOX_EXEC_ID";
2281
+ const SANDBOX_CHILD_INTERRUPT_POLL_MS = 50;
2068
2282
  async function spawnSandboxChild(params) {
2069
2283
  const [command, ...args] = params.argv;
2070
2284
  const finalize = async (status, exitCode) => await params.finalizeExec?.({
@@ -2078,54 +2292,88 @@ async function spawnSandboxChild(params) {
2078
2292
  throw new Error("OpenClaw sandbox exec spec did not provide a command.");
2079
2293
  }
2080
2294
  let child;
2081
- try {
2082
- child = spawn(command, args, {
2083
- detached: process.platform !== "win32",
2084
- env: params.env,
2085
- stdio: [
2086
- "pipe",
2087
- "pipe",
2088
- "pipe"
2089
- ]
2090
- });
2091
- } catch (error) {
2092
- await finalize("failed", null).catch(params.onFinalizeError);
2093
- throw error;
2094
- }
2295
+ let pty;
2296
+ let exitOutcome;
2095
2297
  let outcome;
2096
- const closed = new Promise((resolve) => {
2097
- child.once("close", (code, signal) => resolve(outcome = {
2098
- exitCode: code ?? 1,
2298
+ let escalation;
2299
+ const ready = createDeferred();
2300
+ const exited = createDeferred();
2301
+ const closed = createDeferred();
2302
+ const recordExit = (exitCode, signal) => {
2303
+ if (!exitOutcome) {
2304
+ clearTimeout(escalation);
2305
+ exited.resolve(exitOutcome = {
2306
+ exitCode,
2307
+ signal
2308
+ });
2309
+ }
2310
+ };
2311
+ const recordClose = (exitCode, signal) => {
2312
+ recordExit(exitCode, signal);
2313
+ closed.resolve(outcome = {
2314
+ exitCode,
2099
2315
  signal
2100
- }));
2101
- });
2102
- let finalizePromise;
2316
+ });
2317
+ };
2318
+ let startFailed = false;
2319
+ let startupPending = true;
2320
+ let terminationRequested = false;
2103
2321
  let terminationCleanup;
2104
2322
  let terminationError;
2105
- const settled = closed.then(async (result) => {
2323
+ let settlementStarted = false;
2324
+ const interruptions = /* @__PURE__ */ new Set();
2325
+ const localSignals = [];
2326
+ const settled = closed.promise.then(async (result) => {
2327
+ settlementStarted = true;
2106
2328
  await terminationCleanup;
2107
- child.stdin.destroy();
2108
- await (finalizePromise ??= finalize(params.finalizeStatus(result), result.exitCode));
2329
+ if (interruptions.size > 0) await Promise.allSettled(interruptions);
2330
+ await Promise.all(localSignals);
2331
+ child?.stdin?.destroy();
2332
+ await finalize(startFailed ? "failed" : params.finalizeStatus(result), startFailed ? null : result.exitCode);
2109
2333
  return result;
2110
2334
  });
2111
2335
  settled.catch(params.onFinalizeError);
2336
+ const assertCurrent = () => {
2337
+ params.assertCurrent?.();
2338
+ if (terminationRequested) throw new Error("Sandbox child process start cancelled");
2339
+ };
2112
2340
  let terminationPromise;
2113
2341
  const owner = {
2114
- process: child,
2342
+ assertCurrent,
2343
+ exited: exited.promise,
2344
+ closed: closed.promise,
2115
2345
  settled,
2116
2346
  terminate: () => terminationPromise ??= (async () => {
2117
- child.stdin.destroy();
2347
+ terminationRequested = true;
2348
+ if (startupPending) await ready.promise;
2349
+ if (startFailed || settlementStarted) return await settled;
2350
+ child?.stdin?.destroy();
2118
2351
  terminationCleanup = params.terminateRemote?.().catch((error) => {
2119
2352
  terminationError = error instanceof Error ? error : new Error(String(error));
2120
2353
  });
2121
2354
  await terminationCleanup;
2122
2355
  if (!outcome) {
2123
- if (child.pid) killProcessTree(child.pid, {
2124
- detached: process.platform !== "win32",
2125
- graceMs: SANDBOX_CHILD_TERM_GRACE_MS
2126
- });
2127
- else child.kill("SIGTERM");
2128
- if (!await Promise.race([closed.then(() => true), delay(SANDBOX_CHILD_REAP_TIMEOUT_MS).then(() => false)])) throw new Error(`Sandbox child process tree ${child.pid ?? "unknown"} survived SIGKILL; tear down the sandbox environment and inspect the surviving process tree before retrying.`);
2356
+ const signalLocal = (signal) => {
2357
+ if (exitOutcome) return;
2358
+ if (pty) pty.kill(signal);
2359
+ else if (child?.pid) {
2360
+ const pid = child.pid;
2361
+ localSignals.push(new Promise((resolve) => {
2362
+ signalProcessTree(pid, signal, {
2363
+ detached: process.platform !== "win32",
2364
+ onComplete: resolve
2365
+ });
2366
+ }));
2367
+ } else child?.kill(signal);
2368
+ };
2369
+ signalLocal("SIGTERM");
2370
+ if (!exitOutcome) {
2371
+ escalation = setTimeout(() => signalLocal("SIGKILL"), SANDBOX_CHILD_TERM_GRACE_MS);
2372
+ escalation.unref?.();
2373
+ }
2374
+ const reaped = await Promise.race([closed.promise.then(() => true), delay(SANDBOX_CHILD_REAP_TIMEOUT_MS).then(() => false)]).finally(() => clearTimeout(escalation));
2375
+ await Promise.all(localSignals);
2376
+ if (!reaped) throw new Error(`Sandbox child process tree ${pty?.pid ?? child?.pid ?? "unknown"} survived SIGKILL; tear down the sandbox environment and inspect the surviving process tree before retrying.`);
2129
2377
  }
2130
2378
  const result = await settled;
2131
2379
  if (terminationError) throw terminationError;
@@ -2134,46 +2382,85 @@ async function spawnSandboxChild(params) {
2134
2382
  };
2135
2383
  params.owners.add(owner);
2136
2384
  settled.then(() => params.owners.delete(owner), () => params.owners.delete(owner));
2137
- return owner;
2138
- }
2139
- function prepareSandboxChildExec(backend, env) {
2140
- const marker = randomUUID();
2141
- return {
2142
- env: {
2143
- ...env,
2144
- [SANDBOX_EXEC_MARKER]: marker
2145
- },
2146
- terminate: async () => {
2147
- const result = await backend.runShellCommand({
2148
- script: SANDBOX_REMOTE_TERMINATE_SCRIPT,
2149
- args: [`${SANDBOX_EXEC_MARKER}=${marker}`],
2150
- allowFailure: true,
2151
- signal: AbortSignal.timeout(SANDBOX_CHILD_REAP_TIMEOUT_MS)
2152
- });
2153
- if (result.code !== 0) {
2154
- const detail = result.stderr.toString("utf8").trim() || result.stdout.toString("utf8").trim();
2155
- throw new Error(detail || `Sandbox process tree cleanup failed with code ${result.code}; tear down the sandbox environment and inspect surviving processes before retrying.`);
2385
+ const interrupt = async () => {
2386
+ await ready.promise;
2387
+ const interruptRemote = params.interruptRemote;
2388
+ if (exitOutcome || terminationRequested || !interruptRemote) return;
2389
+ const interruption = (async () => {
2390
+ const deadline = performance.now() + SANDBOX_CHILD_REAP_TIMEOUT_MS;
2391
+ while (true) {
2392
+ if (exitOutcome || terminationRequested) return;
2393
+ const remainingMs = Math.ceil(deadline - performance.now());
2394
+ if (remainingMs <= 0) throw new Error("Sandbox process interrupt timed out waiting for remote process admission");
2395
+ assertCurrent();
2396
+ if (await interruptRemote(remainingMs)) return;
2397
+ await Promise.race([delay(Math.min(SANDBOX_CHILD_INTERRUPT_POLL_MS, Math.max(0, deadline - performance.now()))), exited.promise]);
2156
2398
  }
2399
+ })();
2400
+ interruptions.add(interruption);
2401
+ try {
2402
+ await interruption;
2403
+ } finally {
2404
+ interruptions.delete(interruption);
2157
2405
  }
2158
2406
  };
2407
+ try {
2408
+ if (params.usePty) {
2409
+ const env = Object.fromEntries(Object.entries(params.env).filter((entry) => entry[1] !== void 0));
2410
+ pty = await spawnTerminalPty({
2411
+ file: command,
2412
+ args,
2413
+ cwd: params.cwd,
2414
+ env,
2415
+ cols: 80,
2416
+ rows: 24
2417
+ }, { assertCurrent });
2418
+ pty.onExit(({ exitCode, signal }) => {
2419
+ recordClose(signal ? 1 : exitCode, signal || null);
2420
+ });
2421
+ return {
2422
+ ...owner,
2423
+ pty,
2424
+ interrupt
2425
+ };
2426
+ }
2427
+ assertCurrent();
2428
+ child = spawn(command, args, {
2429
+ detached: process.platform !== "win32",
2430
+ env: params.env,
2431
+ cwd: params.cwd,
2432
+ stdio: [
2433
+ "pipe",
2434
+ "pipe",
2435
+ "pipe"
2436
+ ]
2437
+ });
2438
+ child.once("exit", (code, signal) => recordExit(code ?? 1, signal));
2439
+ child.once("close", (code, signal) => recordClose(code ?? 1, signal));
2440
+ const markStartFailed = () => {
2441
+ startFailed = true;
2442
+ };
2443
+ child.once("error", markStartFailed);
2444
+ try {
2445
+ await once(child, "spawn");
2446
+ } finally {
2447
+ child.off("error", markStartFailed);
2448
+ }
2449
+ return {
2450
+ ...owner,
2451
+ process: child,
2452
+ interrupt
2453
+ };
2454
+ } catch (error) {
2455
+ startFailed = true;
2456
+ if (!child) recordClose(1, null);
2457
+ await settled.catch(() => void 0);
2458
+ throw error;
2459
+ } finally {
2460
+ startupPending = false;
2461
+ ready.resolve();
2462
+ }
2159
2463
  }
2160
- const SANDBOX_REMOTE_TERMINATE_SCRIPT = String.raw`
2161
- find_owned_pids() {
2162
- for env_file in /proc/[0-9]*/environ; do
2163
- if [ -r "$env_file" ] && tr '\0' '\n' < "$env_file" 2>/dev/null | grep -Fqx "$1"; then
2164
- basename "$(dirname "$env_file")"
2165
- fi
2166
- done
2167
- }
2168
- owned="$(find_owned_pids "$1")"
2169
- [ -z "$owned" ] || kill -TERM $owned 2>/dev/null || true
2170
- sleep 1
2171
- owned="$(find_owned_pids "$1")"
2172
- [ -z "$owned" ] || kill -KILL $owned 2>/dev/null || true
2173
- sleep 1
2174
- owned="$(find_owned_pids "$1")"
2175
- [ -z "$owned" ] || { echo "Sandbox process IDs survived SIGKILL: $owned" >&2; exit 1; }
2176
- `.trim();
2177
2464
  function delay(ms) {
2178
2465
  return new Promise((resolve) => {
2179
2466
  setTimeout(resolve, ms).unref?.();
@@ -2188,7 +2475,7 @@ function delay(ms) {
2188
2475
  /** Maximum JSON-line size accepted from the streaming HTTP helper process. */
2189
2476
  const SANDBOX_HTTP_STREAM_LINE_MAX_CHARS = 262144;
2190
2477
  /** Handles one sandbox HTTP JSON-RPC request, optionally streaming response body deltas. */
2191
- async function httpRequest(execServer, notifications, params) {
2478
+ async function httpRequest(execServer, notifications, params, operations) {
2192
2479
  const record = requireObject(params, "http/request params");
2193
2480
  const requestId = requireString(record.requestId, "requestId");
2194
2481
  const url = requireString(record.url, "url");
@@ -2204,11 +2491,14 @@ async function httpRequest(execServer, notifications, params) {
2204
2491
  redirectPolicy,
2205
2492
  streamResponse: record.streamResponse === true
2206
2493
  };
2207
- if (request.streamResponse) return await runStreamingSandboxHttpRequest(execServer, notifications, requestId, request);
2208
- return await runSandboxHttpRequest(execServer, {
2209
- ...request,
2210
- streamResponse: false
2494
+ const response = createDeferred();
2495
+ const operation = runSandboxHttpRequest(execServer, notifications, requestId, request, response);
2496
+ operations.add(operation);
2497
+ operation.then(() => operations.delete(operation), (error) => {
2498
+ operations.delete(operation);
2499
+ response.reject(error);
2211
2500
  });
2501
+ return await response.promise;
2212
2502
  }
2213
2503
  function assertSandboxHttpRequestTargetAllowed(url) {
2214
2504
  let parsed;
@@ -2220,50 +2510,54 @@ function assertSandboxHttpRequestTargetAllowed(url) {
2220
2510
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new SsrFBlockedError(`Blocked non-HTTP(S) protocol in sandbox http/request: ${parsed.protocol}`);
2221
2511
  if (isBlockedHostnameOrIp(parsed.hostname)) throw new SsrFBlockedError(`Blocked hostname or private/internal IP in sandbox http/request: ${parsed.hostname}`);
2222
2512
  }
2223
- async function runSandboxHttpRequest(execServer, params) {
2224
- const result = await execServer.backend.runShellCommand({
2225
- script: SANDBOX_HTTP_REQUEST_SCRIPT,
2226
- stdin: JSON.stringify(params),
2227
- allowFailure: true
2228
- });
2229
- if (result.code !== 0) {
2230
- const stderr = result.stderr.toString("utf8").trim();
2231
- throw new Error(stderr || `sandbox http/request failed with code ${result.code}`);
2232
- }
2233
- const parsed = JSON.parse(result.stdout.toString("utf8"));
2234
- if (typeof parsed.status !== "number" || !Array.isArray(parsed.headers)) throw new Error("sandbox http/request returned an invalid response envelope");
2235
- return {
2236
- status: parsed.status,
2237
- headers: readHttpHeaders(parsed.headers),
2238
- bodyBase64: typeof parsed.bodyBase64 === "string" ? parsed.bodyBase64 : ""
2239
- };
2240
- }
2241
- async function runStreamingSandboxHttpRequest(execServer, notifications, requestId, params) {
2242
- const backend = execServer.backend;
2243
- const remoteExec = prepareSandboxChildExec(backend, {});
2244
- const execSpec = await backend.buildExecSpec({
2245
- command: SANDBOX_HTTP_REQUEST_SCRIPT,
2246
- workdir: execServer.sandbox.containerWorkdir,
2247
- env: remoteExec.env,
2248
- usePty: false
2249
- });
2513
+ async function runSandboxHttpRequest(execServer, notifications, requestId, params, response) {
2250
2514
  const lifecycle = { failed: false };
2251
- const owner = await spawnSandboxChild({
2252
- argv: execSpec.argv,
2253
- env: execSpec.env,
2254
- finalizeExec: backend.finalizeExec,
2255
- finalizeToken: execSpec.finalizeToken,
2256
- finalizeStatus: (outcome) => lifecycle.failed || outcome.exitCode !== 0 ? "failed" : "completed",
2257
- onFinalizeError: (error) => {
2258
- embeddedAgentLog.warn("codex sandbox http/request finalize failed", { error });
2259
- },
2260
- owners: execServer.children,
2261
- terminateRemote: remoteExec.terminate
2262
- });
2515
+ let owner;
2516
+ try {
2517
+ notifications.signal.throwIfAborted();
2518
+ const backend = execServer.backend;
2519
+ const remoteExec = prepareSandboxProcessCleanup(backend, {});
2520
+ const execSpec = await backend.buildExecSpec({
2521
+ command: SANDBOX_HTTP_REQUEST_SCRIPT,
2522
+ workdir: execServer.sandbox.containerWorkdir,
2523
+ env: remoteExec.env,
2524
+ usePty: false
2525
+ });
2526
+ owner = await spawnSandboxChild({
2527
+ argv: execSpec.argv,
2528
+ env: execSpec.env,
2529
+ cwd: execSpec.cwd,
2530
+ assertCurrent: () => {
2531
+ notifications.signal.throwIfAborted();
2532
+ execSpec.assertCurrent?.();
2533
+ },
2534
+ finalizeExec: backend.finalizeExec,
2535
+ finalizeToken: execSpec.finalizeToken,
2536
+ finalizeStatus: (outcome) => lifecycle.failed || outcome.exitCode !== 0 ? "failed" : "completed",
2537
+ onFinalizeError: (error) => {
2538
+ embeddedAgentLog.warn("codex sandbox http/request finalize failed", { error });
2539
+ },
2540
+ owners: execServer.children,
2541
+ terminateRemote: remoteExec.terminate
2542
+ });
2543
+ } catch (error) {
2544
+ response.reject(error);
2545
+ return;
2546
+ }
2263
2547
  const child = owner.process;
2548
+ const completion = createDeferred();
2549
+ owner.settled.then(() => completion.resolve(), completion.reject);
2550
+ let termination;
2551
+ const terminate = () => {
2552
+ if (!termination) {
2553
+ termination = owner.terminate().then(() => void 0);
2554
+ termination.then(completion.resolve, completion.reject);
2555
+ }
2556
+ return termination;
2557
+ };
2264
2558
  const abortOnSessionClose = () => {
2265
2559
  lifecycle.failed = true;
2266
- owner.terminate().catch((error) => {
2560
+ terminate().catch((error) => {
2267
2561
  embeddedAgentLog.warn("codex sandbox http/request cleanup failed", { error });
2268
2562
  });
2269
2563
  };
@@ -2271,21 +2565,38 @@ async function runStreamingSandboxHttpRequest(execServer, notifications, request
2271
2565
  child.once("close", () => {
2272
2566
  notifications.signal.removeEventListener("abort", abortOnSessionClose);
2273
2567
  });
2274
- if (notifications.signal.aborted) abortOnSessionClose();
2275
2568
  child.stdin.on("error", (error) => {
2276
2569
  if (error.code === "EPIPE" || error.code === "ERR_STREAM_DESTROYED") return;
2277
2570
  embeddedAgentLog.warn("codex sandbox http/request stdin write failed", { error });
2278
2571
  });
2279
- child.stdin.end(JSON.stringify(params));
2280
- return await readStreamingSandboxHttpResponse({
2572
+ readSandboxHttpResponse({
2281
2573
  child,
2282
2574
  lifecycle,
2283
- owner,
2575
+ terminate,
2284
2576
  requestId,
2285
- notifications
2286
- });
2577
+ notifications,
2578
+ streamResponse: params.streamResponse
2579
+ }).then(response.resolve, response.reject);
2580
+ try {
2581
+ if (notifications.signal.aborted) abortOnSessionClose();
2582
+ else {
2583
+ owner.assertCurrent();
2584
+ child.stdin.end(JSON.stringify(params));
2585
+ }
2586
+ await completion.promise;
2587
+ await termination;
2588
+ } catch (error) {
2589
+ lifecycle.failed = true;
2590
+ response.reject(error);
2591
+ await terminate().catch((cleanupError) => {
2592
+ embeddedAgentLog.warn("codex sandbox http/request cleanup failed", { error: cleanupError });
2593
+ });
2594
+ throw error;
2595
+ } finally {
2596
+ notifications.signal.removeEventListener("abort", abortOnSessionClose);
2597
+ }
2287
2598
  }
2288
- function readStreamingSandboxHttpResponse(params) {
2599
+ function readSandboxHttpResponse(params) {
2289
2600
  return new Promise((resolve, reject) => {
2290
2601
  let headerResolved = false;
2291
2602
  let failed = false;
@@ -2293,11 +2604,21 @@ function readStreamingSandboxHttpResponse(params) {
2293
2604
  let lastBodySeq = 0;
2294
2605
  let stdoutBuffer = "";
2295
2606
  let stderr = "";
2296
- const fail = (message, _exitCode) => {
2607
+ const buffered = {
2608
+ stdout: {
2609
+ chunks: [],
2610
+ bytes: 0
2611
+ },
2612
+ stderr: {
2613
+ chunks: [],
2614
+ bytes: 0
2615
+ }
2616
+ };
2617
+ const fail = (message) => {
2297
2618
  if (failed) return;
2298
2619
  failed = true;
2299
2620
  params.lifecycle.failed = true;
2300
- params.owner.terminate().catch((error) => {
2621
+ params.terminate().catch((error) => {
2301
2622
  embeddedAgentLog.warn("codex sandbox http/request cleanup failed", { error });
2302
2623
  });
2303
2624
  if (headerResolved) {
@@ -2312,9 +2633,28 @@ function readStreamingSandboxHttpResponse(params) {
2312
2633
  }
2313
2634
  reject(new Error(message));
2314
2635
  };
2315
- params.child.stdout.setEncoding("utf8");
2636
+ const bufferOutput = (stream, chunk) => {
2637
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
2638
+ const output = buffered[stream];
2639
+ output.bytes += buffer.byteLength;
2640
+ if (output.bytes > SANDBOX_COMMAND_MAX_BUFFER_BYTES) {
2641
+ fail(`sandbox http/request ${stream} exceeded ${SANDBOX_COMMAND_MAX_BUFFER_BYTES} bytes`);
2642
+ return;
2643
+ }
2644
+ output.chunks.push(buffer);
2645
+ };
2646
+ if (params.streamResponse) {
2647
+ params.child.stdout.setEncoding("utf8");
2648
+ params.child.stderr.setEncoding("utf8");
2649
+ }
2316
2650
  params.child.stdout.on("data", (chunk) => {
2317
- stdoutBuffer += chunk;
2651
+ if (failed) return;
2652
+ if (!params.streamResponse) {
2653
+ bufferOutput("stdout", chunk);
2654
+ return;
2655
+ }
2656
+ const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
2657
+ stdoutBuffer += text;
2318
2658
  let newline = stdoutBuffer.indexOf("\n");
2319
2659
  while (newline >= 0) {
2320
2660
  const line = stdoutBuffer.slice(0, newline).trim();
@@ -2341,15 +2681,20 @@ function readStreamingSandboxHttpResponse(params) {
2341
2681
  });
2342
2682
  }
2343
2683
  } catch (error) {
2344
- fail(error instanceof Error ? error.message : String(error), null);
2684
+ fail(error instanceof Error ? error.message : String(error));
2345
2685
  }
2346
2686
  newline = stdoutBuffer.indexOf("\n");
2347
2687
  }
2348
- if (stdoutBuffer.length > SANDBOX_HTTP_STREAM_LINE_MAX_CHARS) fail(`sandbox http/request produced an unterminated stdout line longer than ${SANDBOX_HTTP_STREAM_LINE_MAX_CHARS} characters`, null);
2688
+ if (stdoutBuffer.length > SANDBOX_HTTP_STREAM_LINE_MAX_CHARS) fail(`sandbox http/request produced an unterminated stdout line longer than ${SANDBOX_HTTP_STREAM_LINE_MAX_CHARS} characters`);
2349
2689
  });
2350
- params.child.stderr.setEncoding("utf8");
2351
2690
  params.child.stderr.on("data", (chunk) => {
2352
- stderr = sliceUtf16Safe(`${stderr}${chunk}`, -4096);
2691
+ if (failed) return;
2692
+ if (!params.streamResponse) {
2693
+ bufferOutput("stderr", chunk);
2694
+ return;
2695
+ }
2696
+ const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
2697
+ stderr = sliceUtf16Safe(`${stderr}${text}`, -4096);
2353
2698
  });
2354
2699
  params.child.once("error", (error) => {
2355
2700
  childFailure ??= error.message;
@@ -2359,17 +2704,32 @@ function readStreamingSandboxHttpResponse(params) {
2359
2704
  const exitCode = code ?? 1;
2360
2705
  if (failed) return;
2361
2706
  if (childFailure) {
2362
- fail(childFailure, exitCode);
2707
+ fail(childFailure);
2363
2708
  return;
2364
2709
  }
2365
2710
  if (exitCode === 0) {
2711
+ if (!params.streamResponse) {
2712
+ try {
2713
+ const parsed = JSON.parse(Buffer.concat(buffered.stdout.chunks).toString("utf8"));
2714
+ if (typeof parsed.status !== "number" || !Array.isArray(parsed.headers)) throw new Error("sandbox http/request returned an invalid response envelope");
2715
+ resolve({
2716
+ status: parsed.status,
2717
+ headers: readHttpHeaders(parsed.headers),
2718
+ bodyBase64: typeof parsed.bodyBase64 === "string" ? parsed.bodyBase64 : ""
2719
+ });
2720
+ } catch (error) {
2721
+ fail(error instanceof Error ? error.message : String(error));
2722
+ }
2723
+ return;
2724
+ }
2366
2725
  if (!headerResolved) {
2367
2726
  params.lifecycle.failed = true;
2368
2727
  reject(/* @__PURE__ */ new Error("sandbox http/request exited before returning headers"));
2369
2728
  }
2370
2729
  return;
2371
2730
  }
2372
- fail(stderr.trim() || `sandbox http/request failed with code ${exitCode}`, exitCode);
2731
+ if (!params.streamResponse) stderr = Buffer.concat(buffered.stderr.chunks).toString("utf8");
2732
+ fail(stderr.trim() || `sandbox http/request failed with code ${exitCode}`);
2373
2733
  });
2374
2734
  });
2375
2735
  }
@@ -2617,7 +2977,7 @@ const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
2617
2977
  const RETAINED_PROCESS_OUTPUT_BYTES = 1048576;
2618
2978
  const CLOSED_PROCESS_EVICTION_MS = 6e4;
2619
2979
  /** Starts a sandbox-backed process and registers it in the connection-local process table. */
2620
- async function startProcess(execServer, processes, notify, params) {
2980
+ async function startProcess(execServer, processes, notify, params, processAuthority) {
2621
2981
  const record = requireObject(params, "process/start params");
2622
2982
  const processId = requireString(record.processId, "processId");
2623
2983
  if (processes.has(processId)) throw new Error(`process already exists: ${processId}`);
@@ -2649,18 +3009,39 @@ async function startProcess(execServer, processes, notify, params) {
2649
3009
  managed.evictionTimer.unref?.();
2650
3010
  }
2651
3011
  };
3012
+ const source = processAuthority?.claim(record.metadata, async () => {
3013
+ await terminateManagedProcess(managed);
3014
+ });
2652
3015
  processes.set(processId, managed);
2653
3016
  const startPromise = runProcess(execServer, managed, {
2654
3017
  argv,
2655
3018
  cwd,
2656
- env
3019
+ env,
3020
+ source
2657
3021
  });
2658
3022
  managed.startPromise = startPromise;
3023
+ startPromise.catch(() => void 0).then(async () => {
3024
+ await managed.child?.settled;
3025
+ if (managed.terminationRequested) await managed.child?.terminate();
3026
+ source?.settle();
3027
+ }).catch((error) => {
3028
+ source?.fail(error);
3029
+ embeddedAgentLog.warn("codex sandbox process settlement failed", {
3030
+ processId,
3031
+ error: coerceErrorMessage(error)
3032
+ });
3033
+ });
2659
3034
  try {
2660
3035
  await startPromise;
2661
3036
  } catch (error) {
2662
- processes.delete(processId);
2663
3037
  managed.failure = coerceErrorMessage(error);
3038
+ await managed.child?.terminate().catch((cleanupError) => {
3039
+ embeddedAgentLog.warn("codex sandbox failed-start cleanup failed", {
3040
+ processId,
3041
+ error: coerceErrorMessage(cleanupError)
3042
+ });
3043
+ });
3044
+ processes.delete(processId);
2664
3045
  managed.exitCode = null;
2665
3046
  managed.exited = true;
2666
3047
  managed.closed = true;
@@ -2684,13 +3065,14 @@ function assertSupportedProcessSandbox(execServer, record) {
2684
3065
  }
2685
3066
  async function runProcess(execServer, managed, params) {
2686
3067
  const backend = execServer.backend;
3068
+ params.source?.assertAdmission();
2687
3069
  throwIfProcessStartCancelled(managed);
2688
- const remoteExec = prepareSandboxChildExec(backend, params.env);
3070
+ const remoteExec = prepareSandboxProcessCleanup(backend, params.env);
2689
3071
  const execSpec = await backend.buildExecSpec({
2690
- command: buildRemoteCommand(params.argv),
3072
+ command: `exec ${buildRemoteCommand(params.argv)}`,
2691
3073
  workdir: params.cwd,
2692
3074
  env: remoteExec.env,
2693
- usePty: false
3075
+ usePty: managed.tty
2694
3076
  });
2695
3077
  if (managed.terminationRequested) {
2696
3078
  await backend.finalizeExec?.({
@@ -2701,9 +3083,18 @@ async function runProcess(execServer, managed, params) {
2701
3083
  });
2702
3084
  throw new Error("process start cancelled");
2703
3085
  }
3086
+ let spawned = false;
2704
3087
  const owner = await spawnSandboxChild({
2705
3088
  argv: execSpec.argv,
2706
3089
  env: execSpec.env,
3090
+ cwd: execSpec.cwd,
3091
+ usePty: managed.tty,
3092
+ assertCurrent: () => {
3093
+ if (spawned) params.source?.assertCurrent();
3094
+ else params.source?.assertAdmission();
3095
+ execSpec.assertCurrent?.();
3096
+ throwIfProcessStartCancelled(managed);
3097
+ },
2707
3098
  finalizeExec: backend.finalizeExec,
2708
3099
  finalizeToken: execSpec.finalizeToken,
2709
3100
  finalizeStatus: () => managed.failure ? "failed" : "completed",
@@ -2716,19 +3107,29 @@ async function runProcess(execServer, managed, params) {
2716
3107
  });
2717
3108
  },
2718
3109
  owners: execServer.children,
2719
- terminateRemote: remoteExec.terminate
3110
+ terminateRemote: remoteExec.terminate,
3111
+ interruptRemote: remoteExec.interrupt
2720
3112
  });
3113
+ spawned = true;
2721
3114
  managed.child = owner;
3115
+ owner.exited.then(({ exitCode }) => emitProcessExited(managed, exitCode));
3116
+ owner.closed.then(({ exitCode }) => emitProcessClosed(managed, exitCode));
3117
+ if ("pty" in owner) {
3118
+ owner.pty.onData((chunk) => appendProcessChunk(managed, "pty", Buffer.from(chunk)));
3119
+ return;
3120
+ }
2722
3121
  const child = owner.process;
2723
- child.stdout.on("data", (chunk) => appendProcessChunk(managed, managed.tty ? "pty" : "stdout", chunk));
3122
+ child.stdout.on("data", (chunk) => appendProcessChunk(managed, "stdout", chunk));
2724
3123
  child.stderr.on("data", (chunk) => appendProcessChunk(managed, "stderr", chunk));
2725
3124
  child.once("error", (error) => {
2726
3125
  managed.failure ??= error.message;
2727
3126
  notifyProcessWaiters(managed);
2728
3127
  });
2729
- child.once("close", (code) => {
2730
- emitProcessClosed(managed, code ?? 1);
3128
+ child.stdin.on("error", (error) => {
3129
+ managed.failure ??= error.message;
3130
+ notifyProcessWaiters(managed);
2731
3131
  });
3132
+ owner.assertCurrent();
2732
3133
  if (!managed.tty && !managed.pipeStdin) child.stdin.end();
2733
3134
  }
2734
3135
  function throwIfProcessStartCancelled(managed) {
@@ -2757,7 +3158,7 @@ function appendProcessChunk(managed, stream, data) {
2757
3158
  });
2758
3159
  notifyProcessWaiters(managed);
2759
3160
  }
2760
- function emitProcessClosed(managed, exitCode) {
3161
+ function emitProcessExited(managed, exitCode) {
2761
3162
  if (!managed.exited) {
2762
3163
  const exitSeq = managed.nextSeq;
2763
3164
  managed.nextSeq += 1;
@@ -2766,9 +3167,14 @@ function emitProcessClosed(managed, exitCode) {
2766
3167
  if (exitCode !== null) managed.emitNotification("process/exited", {
2767
3168
  processId: managed.processId,
2768
3169
  seq: exitSeq,
2769
- exitCode
3170
+ exitCode,
3171
+ sandboxDenied: false
2770
3172
  });
2771
3173
  }
3174
+ notifyProcessWaiters(managed);
3175
+ }
3176
+ function emitProcessClosed(managed, exitCode) {
3177
+ emitProcessExited(managed, exitCode);
2772
3178
  if (!managed.closed) {
2773
3179
  const closeSeq = managed.nextSeq;
2774
3180
  managed.nextSeq += 1;
@@ -2800,7 +3206,7 @@ async function readProcess(processes, params) {
2800
3206
  const managed = requireProcess(processes, requireString(record.processId, "processId"));
2801
3207
  const afterSeq = typeof record.afterSeq === "number" ? record.afterSeq : 0;
2802
3208
  const waitMs = typeof record.waitMs === "number" && record.waitMs > 0 ? record.waitMs : 0;
2803
- if (!managed.exited && !hasChunksAtOrAfter(managed, afterSeq) && waitMs > 0) await waitForProcessUpdate(managed, waitMs);
3209
+ if (!managed.closed && managed.nextSeq - 1 <= afterSeq && waitMs > 0) await waitForProcessUpdate(managed, waitMs);
2804
3210
  const chunks = limitProcessChunks(managed.chunks.filter((chunk) => chunk.seq > afterSeq), typeof record.maxBytes === "number" && record.maxBytes > 0 ? record.maxBytes : void 0);
2805
3211
  const lastChunk = chunks.at(-1);
2806
3212
  return {
@@ -2819,15 +3225,37 @@ function writeProcess(processes, params) {
2819
3225
  const managed = processes.get(processId);
2820
3226
  if (!managed) return { status: "unknownProcess" };
2821
3227
  const chunk = Buffer.from(requireString(record.chunk, "chunk"), "base64");
2822
- if (!managed.tty && !managed.pipeStdin || managed.closed || !managed.child?.process.stdin.writable) return { status: "stdinClosed" };
2823
- managed.child.process.stdin.write(chunk);
3228
+ if (!managed.tty && !managed.pipeStdin || managed.closed || !managed.child) return { status: "stdinClosed" };
3229
+ if ("pty" in managed.child) {
3230
+ managed.child.assertCurrent();
3231
+ managed.child.pty.write(chunk);
3232
+ } else {
3233
+ if (!managed.child.process.stdin.writable) return { status: "stdinClosed" };
3234
+ managed.child.assertCurrent();
3235
+ managed.child.process.stdin.write(chunk);
3236
+ }
2824
3237
  return { status: "accepted" };
2825
3238
  }
3239
+ async function signalProcess(processes, params) {
3240
+ const record = requireObject(params, "process/signal params");
3241
+ const processId = requireString(record.processId, "processId");
3242
+ if (record.signal !== "interrupt") throw new Error("process/signal only supports interrupt");
3243
+ const managed = processes.get(processId);
3244
+ if (managed && !managed.exited) {
3245
+ await managed.startPromise;
3246
+ managed.child?.assertCurrent();
3247
+ await managed.child?.interrupt();
3248
+ }
3249
+ return {};
3250
+ }
2826
3251
  /** Requests process termination and reports whether it was running at call time. */
2827
3252
  async function terminateProcess(processes, params) {
2828
3253
  const processId = requireString(requireObject(params, "process/terminate params").processId, "processId");
2829
3254
  const managed = processes.get(processId);
2830
3255
  if (!managed) return { running: false };
3256
+ return await terminateManagedProcess(managed);
3257
+ }
3258
+ async function terminateManagedProcess(managed) {
2831
3259
  const running = !managed.exited;
2832
3260
  managed.terminationRequested = true;
2833
3261
  await managed.startPromise?.catch(() => void 0);
@@ -2851,9 +3279,6 @@ function notifyProcessWaiters(managed) {
2851
3279
  managed.waiters = [];
2852
3280
  for (const waiter of waiters) waiter();
2853
3281
  }
2854
- function hasChunksAtOrAfter(managed, afterSeq) {
2855
- return managed.chunks.some((chunk) => chunk.seq > afterSeq);
2856
- }
2857
3282
  function requireProcess(processes, processId) {
2858
3283
  const managed = processes.get(processId);
2859
3284
  if (!managed) throw new Error(`unknown process: ${processId}`);
@@ -2902,11 +3327,13 @@ function readStringList(value) {
2902
3327
  /** Owns the JSON-RPC protocol and resources of one sandbox execution connection. */
2903
3328
  /** Connection-local execution state; closing it never enables session resumption. */
2904
3329
  var CodexSandboxExecSession = class {
2905
- constructor(execServer, transport) {
3330
+ constructor(execServer, transport, processAuthority) {
2906
3331
  this.execServer = execServer;
2907
3332
  this.transport = transport;
3333
+ this.processAuthority = processAuthority;
2908
3334
  this.processes = /* @__PURE__ */ new Map();
2909
3335
  this.fileReads = /* @__PURE__ */ new Map();
3336
+ this.httpRequests = /* @__PURE__ */ new Set();
2910
3337
  this.closeController = new AbortController();
2911
3338
  this.notifications = {
2912
3339
  isOpen: transport.isOpen,
@@ -2941,7 +3368,10 @@ var CodexSandboxExecSession = class {
2941
3368
  if (!this.cleanup) {
2942
3369
  this.closeController.abort();
2943
3370
  closeAllFileReads(this.fileReads);
2944
- this.cleanup = Promise.all([...this.processes.keys()].map(async (processId) => terminateProcess(this.processes, { processId }))).then(() => void 0);
3371
+ this.cleanup = Promise.allSettled([...this.httpRequests, ...[...this.processes.keys()].map(async (processId) => terminateProcess(this.processes, { processId }))]).then((results) => {
3372
+ const failures = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
3373
+ if (failures.length > 0) throw new AggregateError(failures, "Codex sandbox execution cleanup failed");
3374
+ });
2945
3375
  }
2946
3376
  return this.cleanup;
2947
3377
  }
@@ -2957,9 +3387,10 @@ var CodexSandboxExecSession = class {
2957
3387
  capabilities: { networkProxyLaunch: false }
2958
3388
  };
2959
3389
  case "environment/status": return { status: "ready" };
2960
- case "process/start": return startProcess(this.execServer, this.processes, this.notifications.send, params);
3390
+ case "process/start": return startProcess(this.execServer, this.processes, this.notifications.send, params, this.processAuthority);
2961
3391
  case "process/read": return await readProcess(this.processes, params);
2962
3392
  case "process/write": return writeProcess(this.processes, params);
3393
+ case "process/signal": return await signalProcess(this.processes, params);
2963
3394
  case "process/terminate": return await terminateProcess(this.processes, params);
2964
3395
  case "fs/open": return await openFile(this.execServer, this.fileReads, params);
2965
3396
  case "fs/readBlock": return readFileBlock(this.fileReads, params);
@@ -2979,7 +3410,7 @@ var CodexSandboxExecSession = class {
2979
3410
  case "fs/copy":
2980
3411
  await copyPath(this.execServer, params);
2981
3412
  return {};
2982
- case "http/request": return await httpRequest(this.execServer, this.notifications, params);
3413
+ case "http/request": return await httpRequest(this.execServer, this.notifications, params, this.httpRequests);
2983
3414
  default: throw new JsonRpcProtocolError(JSON_RPC_METHOD_NOT_FOUND, `Unsupported OpenClaw sandbox exec-server method: ${method}`);
2984
3415
  }
2985
3416
  }
@@ -3006,9 +3437,17 @@ async function ensureCodexSandboxExecServerEnvironment(params) {
3006
3437
  signal: params.signal,
3007
3438
  onExecutionDisconnect: params.onExecutionDisconnect
3008
3439
  });
3009
- const environmentId = nodeLease ? `openclaw-node-${nodeLease.id}` : execServer.environmentId;
3440
+ const processAuthority = params.requireProcessAuthority && !("node" in execServer) ? getCodexNativeProcessClient(params.client) : void 0;
3441
+ if (processAuthority && !("node" in execServer)) {
3442
+ const authorities = execServer.processAuthorities ??= /* @__PURE__ */ new Map();
3443
+ if (!authorities.has(processAuthority.authPath)) {
3444
+ authorities.set(processAuthority.authPath, processAuthority);
3445
+ params.client.addCloseHandler(() => authorities.delete(processAuthority.authPath));
3446
+ }
3447
+ }
3448
+ const environmentId = nodeLease ? `openclaw-node-${nodeLease.id}` : processAuthority ? `${execServer.environmentId}-${processAuthority.id}` : execServer.environmentId;
3010
3449
  try {
3011
- const execServerUrl = nodeLease ? `${execServer.url}?lease=${nodeLease.id}` : execServer.url;
3450
+ const execServerUrl = nodeLease ? `${execServer.url}?lease=${nodeLease.id}` : processAuthority ? new URL(processAuthority.authPath, execServer.url).href : execServer.url;
3012
3451
  await params.client.request("environment/add", {
3013
3452
  environmentId,
3014
3453
  execServerUrl
@@ -3180,7 +3619,8 @@ async function startOpenClawExecServer(sandbox) {
3180
3619
  handleNodeConnection(execServer, socket, request);
3181
3620
  return;
3182
3621
  }
3183
- handleConnection(execServer, socket);
3622
+ const requestPath = new URL(request.url ?? "", "ws://127.0.0.1").pathname;
3623
+ handleConnection(execServer, socket, execServer.processAuthorities?.get(requestPath));
3184
3624
  });
3185
3625
  embeddedAgentLog.info("codex sandbox exec-server started", {
3186
3626
  environmentId,
@@ -3202,7 +3642,8 @@ function buildEnvironmentId(sandbox) {
3202
3642
  return `openclaw-sandbox-${createHash("sha256").update(sandbox.runtimeId).digest("hex").slice(0, 16)}`;
3203
3643
  }
3204
3644
  function isAuthorizedExecServerRequest(execServer, request) {
3205
- return new URL(request.url ?? "", "ws://127.0.0.1").pathname === execServer.authPath;
3645
+ const url = new URL(request.url ?? "", "ws://127.0.0.1");
3646
+ return url.pathname === execServer.authPath || !("node" in execServer) && execServer.processAuthorities?.has(url.pathname) === true;
3206
3647
  }
3207
3648
  function readCodexPlacementNodeId(sandbox) {
3208
3649
  if (!("placementExecutionMode" in sandbox) || sandbox.placementExecutionMode !== "remote-exec" || !("placementNodeId" in sandbox) || typeof sandbox.placementNodeId !== "string" || !sandbox.placementNodeId) return;
@@ -3255,11 +3696,11 @@ function handleClosedCodexNodeExecServerLease(execServer, lease, result) {
3255
3696
  closeCodexNodeExecServerLease(execServer, lease);
3256
3697
  }
3257
3698
  }
3258
- function handleConnection(execServer, socket) {
3699
+ function handleConnection(execServer, socket, processAuthority) {
3259
3700
  const session = new CodexSandboxExecSession(execServer, {
3260
3701
  isOpen: () => socket.readyState === socket.OPEN,
3261
3702
  send: (message) => socket.send(JSON.stringify(message))
3262
- });
3703
+ }, processAuthority);
3263
3704
  socket.on("message", (data) => {
3264
3705
  handleMessage(session, data).catch((error) => {
3265
3706
  embeddedAgentLog.warn("codex sandbox exec-server message failed", { error });
@@ -3282,6 +3723,73 @@ async function handleMessage(session, data) {
3282
3723
  await session.handleRequest(parseRequest(buffer.toString("utf8")));
3283
3724
  }
3284
3725
  //#endregion
3726
+ //#region extensions/codex/src/app-server/native-command-approval.ts
3727
+ const PERSISTENT_APPROVAL_TARGET_MAX_LENGTH = 256;
3728
+ function resolveCommandApproval(requestParams, outcome) {
3729
+ if (outcome === "cancelled") return {
3730
+ scope: "cancelled",
3731
+ decision: "cancel"
3732
+ };
3733
+ if (outcome === "denied" || outcome === "unavailable") return {
3734
+ scope: "denied",
3735
+ decision: "decline"
3736
+ };
3737
+ const capabilities = commandApprovalCapabilities(requestParams);
3738
+ if (outcome === "approved-session" && capabilities.repeated) return capabilities.repeated;
3739
+ return capabilities.once ? {
3740
+ scope: "once",
3741
+ decision: "accept"
3742
+ } : {
3743
+ scope: "denied",
3744
+ decision: "decline"
3745
+ };
3746
+ }
3747
+ function commandApprovalAllowedDecisions(requestParams, requiresOneShot) {
3748
+ if (!Array.isArray(requestParams?.availableDecisions)) return;
3749
+ const capabilities = commandApprovalCapabilities(requestParams);
3750
+ const decisions = [];
3751
+ if (capabilities.once) decisions.push("allow-once");
3752
+ if (!requiresOneShot && capabilities.repeated) decisions.push("allow-always");
3753
+ decisions.push("deny");
3754
+ return decisions;
3755
+ }
3756
+ function commandApprovalCapabilities(requestParams) {
3757
+ const available = requestParams?.availableDecisions;
3758
+ if (!Array.isArray(available)) return {
3759
+ once: true,
3760
+ repeated: {
3761
+ scope: "session",
3762
+ decision: "acceptForSession"
3763
+ }
3764
+ };
3765
+ return {
3766
+ once: available.includes("accept"),
3767
+ repeated: available.includes("acceptForSession") ? {
3768
+ scope: "session",
3769
+ decision: "acceptForSession"
3770
+ } : findAvailableCommandPersistentApproval(available)
3771
+ };
3772
+ }
3773
+ function findAvailableCommandPersistentApproval(available) {
3774
+ for (const decision of available) {
3775
+ if (!isJsonObject(decision)) continue;
3776
+ const exec = decision.acceptWithExecpolicyAmendment;
3777
+ const network = decision.applyNetworkPolicyAmendment;
3778
+ const prefix = isJsonObject(exec) ? exec.execpolicy_amendment : void 0;
3779
+ const amendment = isJsonObject(network) ? network.network_policy_amendment : void 0;
3780
+ let target;
3781
+ if (Array.isArray(prefix) && prefix.length > 0 && prefix.every((part) => typeof part === "string")) target = `Command prefix: ${JSON.stringify(prefix)}`;
3782
+ else if (isJsonObject(amendment) && amendment.action === "allow" && typeof amendment.host === "string" && amendment.host.length > 0) target = `Network host: ${JSON.stringify(amendment.host)}`;
3783
+ else continue;
3784
+ if (target.length > PERSISTENT_APPROVAL_TARGET_MAX_LENGTH || sanitizeCodexApprovalVisibleText(target) !== target) continue;
3785
+ return {
3786
+ scope: "persistent",
3787
+ decision,
3788
+ description: `Allow Always requests a persistent allow rule for future sessions.\n${target}`
3789
+ };
3790
+ }
3791
+ }
3792
+ //#endregion
3285
3793
  //#region extensions/codex/src/app-server/approval-bridge.ts
3286
3794
  /**
3287
3795
  * Bridges Codex app-server approval requests into OpenClaw policy hooks and
@@ -3307,12 +3815,14 @@ async function handleCodexAppServerApprovalRequest(params) {
3307
3815
  paramsForRun: params.paramsForRun
3308
3816
  });
3309
3817
  if (params.signal?.aborted) {
3818
+ if (params.signal.reason instanceof CodexServerRequestResolvedError) return;
3310
3819
  recordNativeToolFailureDisposition(params, context, "cancelled");
3311
3820
  return buildApprovalResponse(params.method, context.requestParams, "cancelled");
3312
3821
  }
3313
3822
  let revalidateMutableFileApproval;
3314
3823
  let mutableFileApprovalRequiresOneShot = false;
3315
- const resolvePolicyApproval = async (outcome, message = approvalResolutionMessage(outcome), approvalId) => {
3824
+ let approvalId;
3825
+ const resolvePolicyApproval = async (outcome, message = approvalResolutionMessage(outcome), resolvedApprovalId) => {
3316
3826
  let resolvedOutcome = outcome;
3317
3827
  let resolvedMessage = message;
3318
3828
  if (outcome !== "denied" && revalidateMutableFileApproval) {
@@ -3326,6 +3836,16 @@ async function handleCodexAppServerApprovalRequest(params) {
3326
3836
  resolvedOutcome = "approved-once";
3327
3837
  resolvedMessage = "Codex app-server approval granted for this byte-bound command only.";
3328
3838
  }
3839
+ if (params.method === "item/commandExecution/requestApproval" && resolvedOutcome !== "denied") {
3840
+ const resolution = resolveCommandApproval(context.requestParams, resolvedOutcome);
3841
+ if (resolution.scope === "denied") {
3842
+ resolvedOutcome = "denied";
3843
+ resolvedMessage = "Codex app-server request does not offer the approved scope.";
3844
+ } else if (resolvedOutcome === "approved-session" && resolution.scope === "once") {
3845
+ resolvedOutcome = "approved-once";
3846
+ resolvedMessage = approvalResolutionMessage(resolvedOutcome);
3847
+ } else if (resolution.scope === "persistent") resolvedMessage = "Codex app-server approval granted; persistent policy amendment requested for future sessions.";
3848
+ }
3329
3849
  params.signal?.throwIfAborted();
3330
3850
  if (resolvedOutcome !== "denied") params.paramsForRun.hostCapabilities.assertActive();
3331
3851
  emitApprovalEvent(params.paramsForRun, {
@@ -3333,9 +3853,9 @@ async function handleCodexAppServerApprovalRequest(params) {
3333
3853
  kind: context.kind,
3334
3854
  status: resolvedOutcome === "denied" ? "denied" : "approved",
3335
3855
  title: context.title,
3336
- ...approvalId ? {
3337
- approvalId,
3338
- approvalSlug: approvalId
3856
+ ...resolvedApprovalId ? {
3857
+ approvalId: resolvedApprovalId,
3858
+ approvalSlug: resolvedApprovalId
3339
3859
  } : {},
3340
3860
  ...context.eventDetails,
3341
3861
  ...approvalEventScope(params.method, resolvedOutcome),
@@ -3372,22 +3892,25 @@ async function handleCodexAppServerApprovalRequest(params) {
3372
3892
  return await resolvePolicyApproval("denied", policyOutcome.reason);
3373
3893
  }
3374
3894
  if (policyOutcome?.outcome === "approved-once" || policyOutcome?.outcome === "approved-session") return await resolvePolicyApproval(policyOutcome.outcome);
3375
- if (CONCRETE_TOOL_AUTO_APPROVAL_METHODS.has(params.method) && !readNetworkApprovalContext(requestParams) && params.autoApprove === true) return await resolvePolicyApproval("approved-session", "Codex app-server approval auto-approved by runtime policy.");
3895
+ if (CONCRETE_TOOL_AUTO_APPROVAL_METHODS.has(params.method) && !readNetworkApprovalContext(requestParams) && params.autoApprove === true) return await resolvePolicyApproval("approved-once", "Codex app-server approval auto-approved by runtime policy.");
3896
+ const allowedDecisions = nativeApprovalAllowedDecisions({
3897
+ method: params.method,
3898
+ requestParams,
3899
+ requiresOneShot: mutableFileApprovalRequiresOneShot
3900
+ });
3901
+ const repeatedApproval = params.method === "item/commandExecution/requestApproval" && allowedDecisions?.includes("allow-always") ? commandApprovalCapabilities(requestParams).repeated : void 0;
3376
3902
  const requestResult = await requestPluginApproval({
3377
3903
  hostCapabilities: params.paramsForRun.hostCapabilities,
3378
3904
  signal: params.signal,
3379
3905
  title: context.title,
3380
- description: context.description,
3906
+ description: repeatedApproval?.scope === "persistent" ? `${repeatedApproval.description}\n${context.description}` : context.description,
3381
3907
  severity: context.severity,
3382
3908
  toolName: context.toolName,
3383
3909
  toolCallId: context.approvalId,
3384
- allowedDecisions: nativeApprovalAllowedDecisions({
3385
- method: params.method,
3386
- requestParams,
3387
- requiresOneShot: mutableFileApprovalRequiresOneShot
3388
- })
3910
+ allowedDecisions
3389
3911
  });
3390
- const approvalId = requestResult?.id;
3912
+ approvalId = requestResult?.id;
3913
+ params.signal?.throwIfAborted();
3391
3914
  if (!approvalId) {
3392
3915
  recordNativeToolFailureDisposition(params, context, "failed");
3393
3916
  emitApprovalEvent(params.paramsForRun, {
@@ -3416,16 +3939,16 @@ async function handleCodexAppServerApprovalRequest(params) {
3416
3939
  signal: params.signal,
3417
3940
  hostCapabilities: params.paramsForRun.hostCapabilities
3418
3941
  });
3419
- const approvalTimedOut = !params.signal?.aborted && approvalResult?.terminalReason === "timeout";
3420
- const outcome = params.signal?.aborted ? "cancelled" : mapExecDecisionToOutcome(approvalResult?.decision);
3942
+ params.signal?.throwIfAborted();
3943
+ const approvalTimedOut = approvalResult?.terminalReason === "timeout";
3944
+ const outcome = mapExecDecisionToOutcome(approvalResult?.decision);
3421
3945
  if (approvalTimedOut) recordNativeToolFailureDisposition(params, context, "timed_out", context.approvalKind);
3422
- else if (outcome === "cancelled") recordNativeToolFailureDisposition(params, context, params.signal?.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : "cancelled");
3423
3946
  else if (outcome === "unavailable") recordNativeToolFailureDisposition(params, context, "failed");
3424
3947
  if (outcome === "approved-once" || outcome === "approved-session") return await resolvePolicyApproval(outcome, approvalResolutionMessage(outcome), approvalId);
3425
3948
  emitApprovalEvent(params.paramsForRun, {
3426
3949
  phase: "resolved",
3427
3950
  kind: context.kind,
3428
- status: outcome === "denied" ? "denied" : outcome === "unavailable" ? "unavailable" : outcome === "cancelled" ? "failed" : "approved",
3951
+ status: outcome,
3429
3952
  title: context.title,
3430
3953
  approvalId,
3431
3954
  approvalSlug: approvalId,
@@ -3435,6 +3958,7 @@ async function handleCodexAppServerApprovalRequest(params) {
3435
3958
  });
3436
3959
  return buildApprovalResponse(params.method, context.requestParams, outcome);
3437
3960
  } catch (error) {
3961
+ if (params.signal?.reason instanceof CodexServerRequestResolvedError) return;
3438
3962
  const cancelled = params.signal?.aborted === true;
3439
3963
  recordNativeToolFailureDisposition(params, context, cancelled && params.signal ? resolveCodexToolAbortTerminalReason(params.signal) : "failed");
3440
3964
  emitApprovalEvent(params.paramsForRun, {
@@ -3442,6 +3966,10 @@ async function handleCodexAppServerApprovalRequest(params) {
3442
3966
  kind: context.kind,
3443
3967
  status: cancelled ? "failed" : "unavailable",
3444
3968
  title: context.title,
3969
+ ...approvalId ? {
3970
+ approvalId,
3971
+ approvalSlug: approvalId
3972
+ } : {},
3445
3973
  ...context.eventDetails,
3446
3974
  ...approvalEventScope(params.method, cancelled ? "cancelled" : "denied"),
3447
3975
  message: cancelled ? "Codex app-server approval cancelled because the run stopped." : `Codex app-server approval route failed: ${formatCodexDisplayText(coerceErrorMessage(error))}`
@@ -3458,7 +3986,7 @@ function recordNativeToolFailureDisposition(params, context, disposition, approv
3458
3986
  }
3459
3987
  /** Converts an OpenClaw approval outcome into the app-server method response. */
3460
3988
  function buildApprovalResponse(method, requestParams, outcome) {
3461
- if (method === "item/commandExecution/requestApproval") return { decision: commandApprovalDecision(requestParams, outcome) };
3989
+ if (method === "item/commandExecution/requestApproval") return { decision: resolveCommandApproval(requestParams, outcome).decision };
3462
3990
  if (method === "item/fileChange/requestApproval") return { decision: fileChangeApprovalDecision(outcome) };
3463
3991
  if (method === "item/permissions/requestApproval") {
3464
3992
  if (outcome === "approved-session" || outcome === "approved-once") return {
@@ -3716,28 +4244,13 @@ function stableJsonText(value) {
3716
4244
  return entries.every((entry) => entry !== void 0) ? `{${entries.join(",")}}` : void 0;
3717
4245
  }
3718
4246
  }
3719
- function commandApprovalDecision(requestParams, outcome) {
3720
- if (outcome === "cancelled") return "cancel";
3721
- if (outcome === "denied" || outcome === "unavailable") return "decline";
3722
- const capabilities = commandApprovalCapabilities(requestParams);
3723
- if (outcome === "approved-session" && capabilities.sessionDecision !== void 0) return capabilities.sessionDecision;
3724
- return capabilities.once ? "accept" : "decline";
3725
- }
3726
4247
  function nativeApprovalAllowedDecisions(params) {
3727
4248
  if (params.method === "item/fileChange/requestApproval") return [
3728
4249
  "allow-once",
3729
4250
  "allow-always",
3730
4251
  "deny"
3731
4252
  ];
3732
- if (params.method !== "item/commandExecution/requestApproval") return;
3733
- const available = params.requestParams?.availableDecisions;
3734
- if (!Array.isArray(available)) return;
3735
- const capabilities = commandApprovalCapabilities(params.requestParams);
3736
- const decisions = [];
3737
- if (capabilities.once) decisions.push("allow-once");
3738
- if (!params.requiresOneShot && capabilities.sessionDecision !== void 0) decisions.push("allow-always");
3739
- decisions.push("deny");
3740
- return decisions;
4253
+ return params.method === "item/commandExecution/requestApproval" ? commandApprovalAllowedDecisions(params.requestParams, params.requiresOneShot) : void 0;
3741
4254
  }
3742
4255
  function fileChangeApprovalDecision(outcome) {
3743
4256
  if (outcome === "cancelled") return "cancel";
@@ -3919,22 +4432,6 @@ function isPrivateNetworkHostPattern(value) {
3919
4432
  if (wildcardStripped.startsWith("10.") || wildcardStripped.startsWith("127.") || wildcardStripped.startsWith("192.168.") || wildcardStripped.startsWith("169.254.")) return true;
3920
4433
  return /^172\.(1[6-9]|2\d|3[0-1])\./.test(wildcardStripped);
3921
4434
  }
3922
- function commandApprovalCapabilities(requestParams) {
3923
- const available = requestParams?.availableDecisions;
3924
- if (!Array.isArray(available)) return {
3925
- once: true,
3926
- sessionDecision: "acceptForSession"
3927
- };
3928
- return {
3929
- once: available.includes("accept"),
3930
- ...available.includes("acceptForSession") ? { sessionDecision: "acceptForSession" } : { sessionDecision: findAvailableCommandAmendmentDecision(requestParams) }
3931
- };
3932
- }
3933
- function findAvailableCommandAmendmentDecision(requestParams) {
3934
- const available = requestParams?.availableDecisions;
3935
- if (!Array.isArray(available)) return;
3936
- return available.find((entry) => isJsonObject(entry) && (isJsonObject(entry.acceptWithExecpolicyAmendment) || isJsonObject(entry.applyNetworkPolicyAmendment)));
3937
- }
3938
4435
  function approvalResolutionMessage(outcome) {
3939
4436
  return {
3940
4437
  "approved-session": "Codex app-server approval granted for the session.",
@@ -5019,142 +5516,6 @@ function applyCurrentMessageProvider(toolName, args, currentProvider) {
5019
5516
  provider
5020
5517
  };
5021
5518
  }
5022
- function normalizeRouteToken(value) {
5023
- if (typeof value === "number") return Number.isFinite(value) ? String(value) : void 0;
5024
- const normalized = value?.trim().toLowerCase();
5025
- return normalized ? normalized : void 0;
5026
- }
5027
- function sourceRouteTokens(hookContext) {
5028
- const tokens = /* @__PURE__ */ new Set();
5029
- const currentTarget = normalizeRouteToken(hookContext?.currentMessagingTarget);
5030
- const currentChannel = normalizeRouteToken(hookContext?.currentChannelId);
5031
- const currentProvider = normalizeRouteToken(hookContext?.currentChannelProvider);
5032
- if (currentTarget) tokens.add(currentTarget);
5033
- if (currentChannel) tokens.add(currentChannel);
5034
- const channelPrefixIndex = currentChannel?.indexOf(":") ?? -1;
5035
- if (channelPrefixIndex >= 0 && currentChannel) {
5036
- const unprefixedChannel = currentChannel.slice(channelPrefixIndex + 1);
5037
- if (unprefixedChannel) {
5038
- tokens.add(unprefixedChannel);
5039
- for (const segment of unprefixedChannel.split(/[;,]/u)) {
5040
- const token = normalizeRouteToken(segment);
5041
- if (token) tokens.add(token);
5042
- }
5043
- }
5044
- }
5045
- if (currentProvider && currentChannel?.startsWith(`${currentProvider}:`)) {
5046
- const unprefixedChannel = currentChannel.slice(currentProvider.length + 1);
5047
- if (unprefixedChannel) tokens.add(unprefixedChannel);
5048
- }
5049
- return tokens;
5050
- }
5051
- function routeTokenMatchesSource(token, hookContext) {
5052
- const normalized = normalizeRouteToken(token);
5053
- return normalized !== void 0 && sourceRouteTokens(hookContext).has(normalized);
5054
- }
5055
- function routeProviderMatchesSource(provider, hookContext) {
5056
- const normalized = normalizeRouteToken(provider);
5057
- if (!normalized) return false;
5058
- const currentProvider = normalizeRouteToken(hookContext?.currentChannelProvider);
5059
- const currentChannel = normalizeRouteToken(hookContext?.currentChannelId);
5060
- return currentProvider === normalized || currentChannel?.startsWith(`${normalized}:`) === true;
5061
- }
5062
- function routeTokenMatchesCurrentMessage(token, hookContext) {
5063
- const normalized = normalizeRouteToken(token);
5064
- return normalized !== void 0 && normalized === normalizeRouteToken(hookContext?.currentMessageId);
5065
- }
5066
- function readRouteToken(record, key) {
5067
- const value = record[key];
5068
- return typeof value === "string" || typeof value === "number" ? value : void 0;
5069
- }
5070
- function explicitRouteTokensMismatchCurrent(args, keys, currentToken) {
5071
- const normalizedCurrent = normalizeRouteToken(currentToken);
5072
- if (!normalizedCurrent) return false;
5073
- return keys.some((key) => {
5074
- const normalized = normalizeRouteToken(readRouteToken(args, key));
5075
- return normalized !== void 0 && normalized !== normalizedCurrent;
5076
- });
5077
- }
5078
- function explicitThreadRouteTargetsNonSource(args, hookContext, messagingTarget) {
5079
- const normalizedCurrentThread = normalizeRouteToken(hookContext?.currentThreadId);
5080
- const explicitThreadTokens = [...EXPLICIT_MESSAGE_THREAD_KEYS.map((key) => normalizeRouteToken(readRouteToken(args, key))), normalizeRouteToken(messagingTarget?.threadId)].filter((value) => value !== void 0);
5081
- if (explicitThreadTokens.length === 0) return false;
5082
- return normalizedCurrentThread === void 0 || explicitThreadTokens.some((value) => value !== normalizedCurrentThread);
5083
- }
5084
- function replyReceiptMatchesCurrentMessage(value, hookContext, depth = 0) {
5085
- if (depth > 4 || value === null) return false;
5086
- if (typeof value === "string") {
5087
- const trimmed = value.trim();
5088
- if (!trimmed || !["{", "["].includes(trimmed[0] ?? "")) return false;
5089
- try {
5090
- return replyReceiptMatchesCurrentMessage(JSON.parse(trimmed), hookContext, depth + 1);
5091
- } catch {
5092
- return false;
5093
- }
5094
- }
5095
- if (typeof value !== "object") return false;
5096
- if (Array.isArray(value)) return value.some((item) => replyReceiptMatchesCurrentMessage(item, hookContext, depth + 1));
5097
- const record = value;
5098
- for (const key of [
5099
- "repliedTo",
5100
- "replyTo",
5101
- "replyToId",
5102
- "replyToIdFull"
5103
- ]) if (routeTokenMatchesCurrentMessage(typeof record[key] === "string" ? record[key] : void 0, hookContext)) return true;
5104
- for (const key of [
5105
- "content",
5106
- "details",
5107
- "payload",
5108
- "receipt",
5109
- "result",
5110
- "results",
5111
- "sendResult",
5112
- "text"
5113
- ]) if (replyReceiptMatchesCurrentMessage(record[key], hookContext, depth + 1)) return true;
5114
- return false;
5115
- }
5116
- function hasExplicitNonSourceMessageRoute(args, hookContext, messagingTarget) {
5117
- const currentProvider = normalizeRouteToken(hookContext?.currentChannelProvider);
5118
- for (const key of EXPLICIT_MESSAGE_PROVIDER_KEYS) {
5119
- const provider = normalizeRouteToken(typeof args[key] === "string" ? args[key] : void 0);
5120
- if (provider && currentProvider !== provider && !routeProviderMatchesSource(provider, hookContext)) return true;
5121
- }
5122
- const targetValues = [...EXPLICIT_MESSAGE_TARGET_KEYS.map((key) => typeof args[key] === "string" ? args[key] : void 0), ...Array.isArray(args.targets) ? args.targets.map((value) => typeof value === "string" ? value : void 0) : []].filter((value) => normalizeRouteToken(value) !== void 0);
5123
- if (explicitThreadRouteTargetsNonSource(args, hookContext, messagingTarget)) return true;
5124
- if (explicitRouteTokensMismatchCurrent(args, EXPLICIT_MESSAGE_REPLY_KEYS, hookContext?.currentMessageId)) return true;
5125
- if (messagingTarget?.to !== void 0 && !routeTokenMatchesSource(messagingTarget.to, hookContext)) return true;
5126
- if (messagingTarget?.to !== void 0) return false;
5127
- if (targetValues.length === 0) return false;
5128
- if (targetValues.some((value) => !routeTokenMatchesSource(value, hookContext))) return true;
5129
- return false;
5130
- }
5131
- function normalizeAcceptedSessionSpawn(result) {
5132
- const details = asOptionalRecord(asOptionalRecord(result)?.details);
5133
- if (!details || details.status !== "accepted") return null;
5134
- const runId = normalizeOptionalString(details.runId);
5135
- const childSessionKey = normalizeOptionalString(details.childSessionKey);
5136
- return runId && childSessionKey ? {
5137
- runId,
5138
- childSessionKey
5139
- } : null;
5140
- }
5141
- const EXPLICIT_MESSAGE_PROVIDER_KEYS = ["channel", "provider"];
5142
- const EXPLICIT_MESSAGE_TARGET_KEYS = [
5143
- "target",
5144
- "to",
5145
- "channelId"
5146
- ];
5147
- const EXPLICIT_MESSAGE_THREAD_KEYS = [
5148
- "threadId",
5149
- "thread_id",
5150
- "messageThreadId",
5151
- "topicId"
5152
- ];
5153
- const EXPLICIT_MESSAGE_REPLY_KEYS = [
5154
- "replyTo",
5155
- "replyToId",
5156
- "replyToIdFull"
5157
- ];
5158
5519
  function computerFrameImageIdentity(content) {
5159
5520
  if (!Array.isArray(content)) return;
5160
5521
  const images = content.filter((block) => block.type === "image");
@@ -5206,6 +5567,7 @@ function createCodexDynamicToolBridge(params) {
5206
5567
  messagingToolSentMediaUrls: [],
5207
5568
  messagingToolSentTargets: [],
5208
5569
  messagingToolSourceReplyPayloads: [],
5570
+ confirmedMediaDeliveries: [],
5209
5571
  toolMediaUrls: [],
5210
5572
  toolAutoDeliveryMediaUrls: [],
5211
5573
  coreTtsToolResults: [],
@@ -5309,16 +5671,16 @@ function createCodexDynamicToolBridge(params) {
5309
5671
  try {
5310
5672
  const prepare = tool.prepareArguments;
5311
5673
  const toolArgs = prepare ? Reflect.apply(prepare, tool, [rawArguments]) : args;
5312
- const preparedArgs = toolName === "message" && isRecord(toolArgs) ? await prepareCodexRemoteWorkspaceMessageMedia({
5674
+ const preparedMessageMedia = toolName === "message" && isRecord(toolArgs) ? await prepareCodexRemoteWorkspaceMessageMedia({
5313
5675
  args: toolArgs,
5314
5676
  localWorkspaceRoot: params.hookContext?.workspaceDir,
5315
5677
  remoteWorkspaceRoot: params.hookContext?.remoteWorkspaceRoot,
5316
5678
  readRemoteFile: readRemoteWorkspaceFile,
5317
5679
  timeoutMs: params.hookContext?.remoteWorkspaceRequestTimeoutMs,
5318
5680
  signal
5319
- }) : toolArgs;
5320
- const telemetryArgs = isRecord(preparedArgs) ? preparedArgs : args;
5321
- executedArgs = structuredClone(telemetryArgs);
5681
+ }) : void 0;
5682
+ const preparedArgs = preparedMessageMedia?.args ?? toolArgs;
5683
+ executedArgs = structuredClone(isRecord(preparedArgs) ? preparedArgs : args);
5322
5684
  const messagingContext = {
5323
5685
  config: params.hookContext?.config,
5324
5686
  currentChannelId: params.hookContext?.currentChannelId,
@@ -5343,10 +5705,41 @@ function createCodexDynamicToolBridge(params) {
5343
5705
  }));
5344
5706
  const rawResult = await Reflect.apply(tool.execute, tool, executionArgs);
5345
5707
  captureExecutionBoundary();
5346
- if (toolName === "message" && asOptionalRecord(asOptionalRecord(rawResult.details)?.messageDelivery)?.sourceReplyDelivered === true) telemetry.sourceReplyDelivered = true;
5347
- const telemetryRawResult = sanitizeToolResult(rawResult);
5348
5708
  const rawIsError = isToolResultError(rawResult);
5709
+ const messageDelivery = readEmbeddedMessageDeliveryFact(asOptionalRecord(asOptionalRecord(rawResult)?.details)?.messageDelivery);
5710
+ const messagingDelivered = isMessagingTool(toolName) && (messageDelivery ? messageDelivery.status === "settled" && (!rawIsError || messageDelivery.partialDelivery) : isDeliveredMessagingToolResult({
5711
+ toolName,
5712
+ args: executedArgs,
5713
+ result: rawResult,
5714
+ isError: rawIsError
5715
+ }));
5716
+ const mediaDelivery = messagingDelivered ? messageDelivery ?? projectPluginMessageDeliveryFact(rawResult) : void 0;
5717
+ const mediaDeliveryConfirmed = messagingDelivered && !rawIsError && executedArgs.dryRun !== true && mediaDelivery?.status === "settled" && !mediaDelivery.partialDelivery;
5718
+ const messagingTelemetryArgs = applyCurrentMessageProvider(toolName, executedArgs, params.hookContext?.currentChannelProvider);
5719
+ const messagingTarget = isMessagingTool(toolName) ? extractMessagingToolSend(toolName, messagingTelemetryArgs, messagingContext) : void 0;
5720
+ const confirmedMessagingTarget = messagingDelivered && messagingTarget ? extractMessagingToolSendResult(messagingTarget, rawResult) : messagingTarget;
5721
+ const deliveredSourceReply = messagingDelivered && isDeliveredMessageToolOnlySourceReplyResult({
5722
+ sourceReplyDeliveryMode: params.hookContext?.sourceReplyDeliveryMode,
5723
+ toolName,
5724
+ args: executedArgs,
5725
+ result: rawResult,
5726
+ isError: rawIsError,
5727
+ deliveryConfirmed: true,
5728
+ allowExplicitSourceRoute: isDeliveredMessagingToolSendToCurrentSource({
5729
+ send: confirmedMessagingTarget,
5730
+ config: params.hookContext?.config,
5731
+ currentProvider: params.hookContext?.currentChannelProvider,
5732
+ currentAccountId: params.hookContext?.turnSourceAccountId,
5733
+ currentChannelId: params.hookContext?.currentChannelId,
5734
+ currentMessagingTarget: params.hookContext?.currentMessagingTarget,
5735
+ currentThreadId: params.hookContext?.currentThreadId,
5736
+ sessionKey: params.hookContext?.sessionKey,
5737
+ deliveredPayload: rawResult
5738
+ })
5739
+ });
5740
+ if (toolName === "message" && messageDelivery?.sourceReplyDelivered) telemetry.sourceReplyDelivered = true;
5349
5741
  const rawResultFailureKind = resolveToolResultFailureKind(rawResult);
5742
+ const telemetryRawResult = sanitizeToolResult(rawResult);
5350
5743
  const middlewareResult = await middlewareRunner.applyToolResultMiddleware({
5351
5744
  threadId: call.threadId,
5352
5745
  turnId: call.turnId,
@@ -5365,7 +5758,7 @@ function createCodexDynamicToolBridge(params) {
5365
5758
  result: middlewareResult
5366
5759
  });
5367
5760
  const resultIsError = rawIsError || isToolResultError(result);
5368
- const acceptedSessionSpawn = toolName === "sessions_spawn" && !rawIsError ? normalizeAcceptedSessionSpawn(telemetryRawResult) : null;
5761
+ const acceptedSessionSpawn = toolName === "sessions_spawn" && !rawIsError ? normalizeAcceptedSessionSpawnResult(telemetryRawResult) : null;
5369
5762
  if (acceptedSessionSpawn) telemetry.acceptedSessionSpawns.push(acceptedSessionSpawn);
5370
5763
  const finalResultFailureKind = resolveToolResultFailureKind(result);
5371
5764
  const resultFailureKind = rawResultFailureKind ?? finalResultFailureKind;
@@ -5398,39 +5791,20 @@ function createCodexDynamicToolBridge(params) {
5398
5791
  toolName,
5399
5792
  toolCallOrdinal: options?.toolCallOrdinal
5400
5793
  });
5401
- const messagingTelemetryArgs = applyCurrentMessageProvider(toolName, executedArgs, params.hookContext?.currentChannelProvider);
5402
- const messagingTarget = isMessagingTool(toolName) ? extractMessagingToolSend(toolName, messagingTelemetryArgs, messagingContext) : void 0;
5403
- const confirmedMessagingTarget = !rawIsError && messagingTarget ? extractMessagingToolSendResult(messagingTarget, telemetryRawResult) : messagingTarget;
5404
- const terminalType = resultFailureKind === "blocked" ? "blocked" : resultIsError ? "error" : "completed";
5794
+ const terminalType = resultFailureKind === "blocked" ? "blocked" : resultIsError || resultFailureKind ? "error" : "completed";
5405
5795
  const contentItems = convertToolContents(result.content, toolResultMaxChars);
5406
5796
  const deliveredFrameImages = contentItems.filter((item) => item.type === "inputImage");
5407
5797
  const finalFrameImageIdentity = computerFrameImageIdentity(result.content);
5408
5798
  if (toolName === "computer" && params.computerContextEpoch?.frameToolCallId === call.callId && (deliveredFrameImages.length !== 1 || finalFrameImageIdentity === void 0 || finalFrameImageIdentity !== params.computerContextEpoch.frameImageIdentity)) invalidateComputerFrame(params.computerContextEpoch);
5409
- const response = withDiagnosticTerminalType({
5799
+ const response = {
5410
5800
  contentItems,
5411
- success: !resultIsError
5412
- }, terminalType);
5413
- withDynamicToolTranscriptDetails(response, asOptionalRecord(sanitizeToolResult(result))?.details);
5414
- withDiagnosticFailureDisposition(response, resultFailureKind);
5415
- const blocksSourceReplyTermination = hasExplicitNonSourceMessageRoute(executedArgs, params.hookContext, confirmedMessagingTarget);
5416
- const deliveredSourceReply = isDeliveredMessageToolOnlySourceReplyResult({
5417
- sourceReplyDeliveryMode: params.hookContext?.sourceReplyDeliveryMode,
5418
- toolName,
5419
- args: executedArgs,
5420
- result,
5421
- hookResult: rawResult,
5422
- isError: resultIsError,
5423
- allowExplicitSourceRoute: !blocksSourceReplyTermination
5424
- });
5425
- const receiptConfirmedSourceReply = params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && normalizeRouteToken(typeof executedArgs.action === "string" ? executedArgs.action : void 0) === "reply" && !resultIsError && !blocksSourceReplyTermination && isDeliveredMessagingToolResult({
5426
- toolName,
5427
- args: executedArgs,
5428
- result,
5429
- hookResult: rawResult,
5430
- isError: resultIsError
5431
- }) && (replyReceiptMatchesCurrentMessage(rawResult, params.hookContext) || replyReceiptMatchesCurrentMessage(result, params.hookContext));
5801
+ success: !resultIsError,
5802
+ diagnosticTerminalType: terminalType,
5803
+ diagnosticTerminalReason: resultFailureKind === "blocked" ? void 0 : resultFailureKind,
5804
+ transcriptDetails: asOptionalRecord(sanitizeToolResult(result))?.details
5805
+ };
5432
5806
  const toolConfirmedSourceReply = params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && !resultIsError && (rawResult.terminate === true || result.terminate === true);
5433
- const confirmedSourceReply = params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && (toolConfirmedSourceReply || deliveredSourceReply || receiptConfirmedSourceReply);
5807
+ const confirmedSourceReply = params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && (toolConfirmedSourceReply || deliveredSourceReply);
5434
5808
  const sourceReplyFinal = confirmedSourceReply ? executedArgs.final !== false : void 0;
5435
5809
  const autoDeliveryTtsMediaUrls = getCoreTtsToolResultMediaUrls(rawResult);
5436
5810
  collectToolTelemetry({
@@ -5441,24 +5815,27 @@ function createCodexDynamicToolBridge(params) {
5441
5815
  telemetry,
5442
5816
  signal,
5443
5817
  isError: resultIsError,
5818
+ messagingDelivered,
5819
+ mediaDeliveryConfirmed,
5820
+ sourcePathsByStagedPath: preparedMessageMedia?.sourcePathsByStagedPath,
5444
5821
  autoDeliveryTtsMediaUrls,
5445
5822
  coreTtsToolResult: autoDeliveryTtsMediaUrls?.length ? rawResult : void 0,
5446
5823
  messagingTarget: confirmedMessagingTarget,
5447
5824
  sourceReplyFinal,
5448
5825
  trustedLocalMediaToolNames: pluginLocalMediaTrustByToolName.get(toolName)
5449
5826
  });
5450
- if (deliveredSourceReply || receiptConfirmedSourceReply || toolConfirmedSourceReply) telemetry.didDeliverSourceReplyViaMessageTool = true;
5827
+ if (deliveredSourceReply || toolConfirmedSourceReply) telemetry.didDeliverSourceReplyViaMessageTool = true;
5451
5828
  const continuesSourceReplyProgress = confirmedSourceReply && sourceReplyFinal === false;
5452
- withDynamicToolTermination(response, (rawResult.terminate === true || result.terminate === true) && !continuesSourceReplyProgress || isToolResultYield(rawResult) || isToolResultYield(result) || confirmedSourceReply && sourceReplyFinal === true);
5829
+ response.terminate = (rawResult.terminate === true || result.terminate === true) && !continuesSourceReplyProgress || isToolResultYield(rawResult) || isToolResultYield(result) || confirmedSourceReply && sourceReplyFinal === true || void 0;
5453
5830
  const asyncStarted = isAsyncStartedToolResult(rawResult) || isAsyncStartedToolResult(result);
5454
- withDynamicToolAsyncStarted(response, asyncStarted);
5831
+ response.asyncStarted = asyncStarted || void 0;
5455
5832
  const replaySafe = executionPrevented || !asyncStarted && isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs);
5456
5833
  copyInternalToolResultState(rawResult, response);
5457
- return withDynamicToolExecutionState(response, {
5458
- executedArguments: executedArgs,
5459
- executionStarted: didStartExecution && !executionPrevented,
5460
- sideEffectEvidence: !replaySafe
5461
- });
5834
+ response.executedArguments = executedArgs;
5835
+ response.executionStarted = didStartExecution && !executionPrevented;
5836
+ response.replaySafe = replaySafe;
5837
+ response.sideEffectEvidence = !replaySafe || void 0;
5838
+ return response;
5462
5839
  } catch (error) {
5463
5840
  const trustedNoStart = consumeTrustedToolNoStartError(error);
5464
5841
  executionPrevented ||= trustedNoStart;
@@ -5466,6 +5843,8 @@ function createCodexDynamicToolBridge(params) {
5466
5843
  if (toolName === "computer" && params.computerContextEpoch?.frameToolCallId === call.callId) invalidateComputerFrame(params.computerContextEpoch);
5467
5844
  const executionDisposition = getBeforeToolCallFailureDisposition(error) ?? (signal.aborted ? resolveCodexToolAbortTerminalReason(signal) : resolveToolExecutionErrorKind(error));
5468
5845
  const errorMessage = formatToolExecutionErrorMessage(error, "OpenClaw dynamic tool call failed.");
5846
+ const operatorHint = readToolOperatorHint(error);
5847
+ if (operatorHint) embeddedAgentLog.error(`[tools] ${toolName} failed: ${errorMessage} ${operatorHint}`);
5469
5848
  executionPrevented = executionPrevented || consumePreExecutionBlockedToolCall(call.callId, toolResultHookContext.runId);
5470
5849
  const failedResult = failedToolResult(errorMessage, executionDisposition);
5471
5850
  finalizeToolTerminalPresentation({
@@ -5491,17 +5870,19 @@ function createCodexDynamicToolBridge(params) {
5491
5870
  startedAt
5492
5871
  });
5493
5872
  const replaySafe = !didStartExecution || executionPrevented || isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs);
5494
- return withDynamicToolExecutionState(withDiagnosticFailureDisposition({
5873
+ return {
5495
5874
  contentItems: [{
5496
5875
  type: "inputText",
5497
5876
  text: errorMessage
5498
5877
  }],
5499
- success: false
5500
- }, executionDisposition), {
5878
+ success: false,
5879
+ diagnosticTerminalType: executionDisposition === "blocked" ? "blocked" : "error",
5880
+ diagnosticTerminalReason: executionDisposition === "blocked" ? void 0 : executionDisposition,
5501
5881
  executedArguments: executedArgs,
5502
5882
  executionStarted: didStartExecution && !executionPrevented,
5503
- sideEffectEvidence: didStartExecution && !replaySafe
5504
- });
5883
+ replaySafe,
5884
+ sideEffectEvidence: didStartExecution && !replaySafe || void 0
5885
+ };
5505
5886
  } finally {
5506
5887
  if (executionSnapshotStates.get(call.callId) === executionSnapshotState && (executionSnapshotState.consumed || !executionSnapshotState.retainAfterCompletion)) executionSnapshotStates.delete(call.callId);
5507
5888
  consumeAdjustedParamsForToolCall(call.callId, toolResultHookContext.runId);
@@ -5554,18 +5935,6 @@ function notifyAgentToolResult(observer, toolName, result, isError) {
5554
5935
  embeddedAgentLog.warn(`onAgentToolResult handler failed: tool=${toolName} error=${message}`);
5555
5936
  }
5556
5937
  }
5557
- function failedToolResult(message, status = "failed") {
5558
- return {
5559
- content: [{
5560
- type: "text",
5561
- text: message
5562
- }],
5563
- details: {
5564
- status,
5565
- error: message
5566
- }
5567
- };
5568
- }
5569
5938
  function reportQuarantinedDynamicTools(params) {
5570
5939
  if (params.tools.length === 0) return;
5571
5940
  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`, {
@@ -5610,13 +5979,12 @@ function composeAbortSignals(...signals) {
5610
5979
  return AbortSignal.any(activeSignals);
5611
5980
  }
5612
5981
  function collectToolTelemetry(params) {
5613
- if (params.isError) return;
5614
5982
  if (!params.isError && params.toolName === "cron" && isCronAddAction(params.args)) params.telemetry.successfulCronAdds = (params.telemetry.successfulCronAdds ?? 0) + 1;
5615
5983
  if (!params.isError && params.toolName === HEARTBEAT_RESPONSE_TOOL_NAME) {
5616
5984
  const response = normalizeHeartbeatToolResponse(params.result?.details);
5617
5985
  if (response) params.telemetry.heartbeatToolResponse = response;
5618
5986
  }
5619
- if (params.result && !params.signal.aborted) {
5987
+ if (!params.isError && params.result && !params.signal.aborted) {
5620
5988
  const media = extractToolResultMediaArtifact(params.result);
5621
5989
  if (media) {
5622
5990
  const mediaUrls = filterToolResultMediaUrls(params.toolName, media.mediaUrls, params.coreTtsToolResult ?? params.mediaTrustResult ?? params.result, params.trustedLocalMediaToolNames);
@@ -5639,24 +6007,20 @@ function collectToolTelemetry(params) {
5639
6007
  if (media.audioAsVoice) params.telemetry.toolAudioAsVoice = true;
5640
6008
  }
5641
6009
  }
5642
- if (!isMessagingTool(params.toolName)) return;
5643
- const isMessagingSendAction = isMessagingToolSendAction(params.toolName, params.args);
5644
- if (!isMessagingSendAction && !params.messagingTarget) return;
5645
- if (!isMessagingSendAction && !isDeliveredMessagingToolResult({
5646
- toolName: params.toolName,
5647
- args: params.args,
5648
- result: params.result,
5649
- hookResult: params.mediaTrustResult,
5650
- isError: params.isError
5651
- })) return;
6010
+ if (!params.messagingDelivered) return;
5652
6011
  params.telemetry.didSendViaMessagingTool = true;
5653
- const sourceReplyPayload = extractInternalSourceReplyPayload(params.result?.details);
5654
- if (sourceReplyPayload) {
6012
+ if (asOptionalRecord(asOptionalRecord(params.mediaTrustResult)?.details)?.sourceReplySink === "internal-ui") {
6013
+ const sourceReplyPayload = extractMessagingToolSourceReplyPayload(params.result);
6014
+ if (!sourceReplyPayload) return;
5655
6015
  const record = {
5656
6016
  ...sourceReplyPayload,
5657
6017
  ...params.sourceReplyFinal !== void 0 ? { sourceReplyFinal: params.sourceReplyFinal } : {}
5658
6018
  };
5659
6019
  params.telemetry.messagingToolSourceReplyPayloads.push(record);
6020
+ if (params.mediaDeliveryConfirmed) params.telemetry.confirmedMediaDeliveries.push({
6021
+ kind: "sourceReply",
6022
+ sourceUrls: resolveCodexMediaSourceUrls(collectCodexMessageMediaUrls(record), params.sourcePathsByStagedPath)
6023
+ });
5660
6024
  return record;
5661
6025
  }
5662
6026
  const text = readFirstString(params.args, [
@@ -5666,7 +6030,7 @@ function collectToolTelemetry(params) {
5666
6030
  "content"
5667
6031
  ]);
5668
6032
  if (text) params.telemetry.messagingToolSentTexts.push(text);
5669
- const mediaUrls = collectMediaUrls(params.args);
6033
+ const mediaUrls = params.mediaDeliveryConfirmed ? collectCodexMessageMediaUrls(params.args) : [];
5670
6034
  params.telemetry.messagingToolSentMediaUrls.push(...mediaUrls);
5671
6035
  const record = {
5672
6036
  ...params.messagingTarget ?? {
@@ -5689,27 +6053,13 @@ function collectToolTelemetry(params) {
5689
6053
  ...params.sourceReplyFinal !== void 0 ? { sourceReplyFinal: params.sourceReplyFinal } : {}
5690
6054
  };
5691
6055
  params.telemetry.messagingToolSentTargets.push(record);
6056
+ if (mediaUrls.length > 0) params.telemetry.confirmedMediaDeliveries.push({
6057
+ kind: "outbound",
6058
+ target: record,
6059
+ sourceUrls: resolveCodexMediaSourceUrls(mediaUrls, params.sourcePathsByStagedPath)
6060
+ });
5692
6061
  return record;
5693
6062
  }
5694
- function extractInternalSourceReplyPayload(details) {
5695
- if (!isRecord(details) || details.sourceReplySink !== "internal-ui") return;
5696
- const rawPayload = details.sourceReply;
5697
- if (!isRecord(rawPayload)) return;
5698
- const text = readFirstString(rawPayload, ["text", "message"]);
5699
- const mediaUrls = collectMediaUrls(rawPayload);
5700
- const mediaUrl = typeof rawPayload.mediaUrl === "string" && rawPayload.mediaUrl.trim() ? rawPayload.mediaUrl.trim() : mediaUrls[0];
5701
- const payload = {
5702
- ...text ? { text } : {},
5703
- ...mediaUrl ? { mediaUrl } : {},
5704
- ...mediaUrls.length > 0 ? { mediaUrls } : {},
5705
- ...rawPayload.audioAsVoice === true ? { audioAsVoice: true } : {},
5706
- ...isRecord(rawPayload.presentation) ? { presentation: rawPayload.presentation } : {},
5707
- ...isRecord(rawPayload.interactive) ? { interactive: rawPayload.interactive } : {},
5708
- ...isRecord(rawPayload.channelData) ? { channelData: rawPayload.channelData } : {},
5709
- ...typeof details.idempotencyKey === "string" && details.idempotencyKey.trim() ? { idempotencyKey: details.idempotencyKey.trim() } : {}
5710
- };
5711
- return text || mediaUrls.length > 0 || payload.presentation || payload.interactive ? payload : void 0;
5712
- }
5713
6063
  function isToolResultYield(result) {
5714
6064
  const details = result.details;
5715
6065
  if (!isRecord(details) || typeof details.status !== "string") return false;
@@ -5719,42 +6069,6 @@ function isAsyncStartedToolResult(result) {
5719
6069
  const details = result.details;
5720
6070
  return isRecord(details) && details.async === true && details.status === "started";
5721
6071
  }
5722
- function withDiagnosticTerminalType(response, terminalType) {
5723
- Object.defineProperty(response, "diagnosticTerminalType", {
5724
- configurable: true,
5725
- enumerable: false,
5726
- value: terminalType
5727
- });
5728
- return response;
5729
- }
5730
- function withDiagnosticFailureDisposition(response, disposition) {
5731
- if (!disposition) return response;
5732
- withDiagnosticTerminalType(response, disposition === "blocked" ? "blocked" : "error");
5733
- if (disposition !== "blocked") Object.defineProperty(response, "diagnosticTerminalReason", {
5734
- configurable: true,
5735
- enumerable: false,
5736
- value: disposition
5737
- });
5738
- return response;
5739
- }
5740
- function withDynamicToolTermination(response, terminate) {
5741
- if (!terminate) return response;
5742
- Object.defineProperty(response, "terminate", {
5743
- configurable: true,
5744
- enumerable: false,
5745
- value: true
5746
- });
5747
- return response;
5748
- }
5749
- function withDynamicToolAsyncStarted(response, asyncStarted) {
5750
- if (!asyncStarted) return response;
5751
- Object.defineProperty(response, "asyncStarted", {
5752
- configurable: true,
5753
- enumerable: false,
5754
- value: true
5755
- });
5756
- return response;
5757
- }
5758
6072
  function normalizeToolResultMaxChars(maxChars) {
5759
6073
  return typeof maxChars === "number" && Number.isFinite(maxChars) && maxChars > 0 ? Math.floor(maxChars) : DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS;
5760
6074
  }
@@ -5852,53 +6166,9 @@ function readFirstString(record, keys) {
5852
6166
  if (typeof value === "number" && Number.isFinite(value)) return String(value);
5853
6167
  }
5854
6168
  }
5855
- function collectMediaUrls(record) {
5856
- const urls = [];
5857
- const pushMediaUrl = (value) => {
5858
- if (typeof value === "string" && value.trim()) urls.push(value.trim());
5859
- };
5860
- const pushAttachment = (value) => {
5861
- if (!value || typeof value !== "object" || Array.isArray(value)) return;
5862
- const attachment = value;
5863
- for (const key of [
5864
- "media",
5865
- "mediaUrl",
5866
- "path",
5867
- "filePath",
5868
- "fileUrl",
5869
- "url"
5870
- ]) pushMediaUrl(attachment[key]);
5871
- };
5872
- for (const key of [
5873
- "media",
5874
- "mediaUrl",
5875
- "media_url",
5876
- "path",
5877
- "filePath",
5878
- "fileUrl",
5879
- "imageUrl",
5880
- "image_url"
5881
- ]) {
5882
- const value = record[key];
5883
- pushMediaUrl(value);
5884
- }
5885
- for (const key of [
5886
- "mediaUrls",
5887
- "media_urls",
5888
- "imageUrls",
5889
- "image_urls"
5890
- ]) {
5891
- const value = record[key];
5892
- if (!Array.isArray(value)) continue;
5893
- for (const entry of value) pushMediaUrl(entry);
5894
- }
5895
- const attachments = record.attachments;
5896
- if (Array.isArray(attachments)) for (const attachment of attachments) pushAttachment(attachment);
5897
- return urls;
5898
- }
5899
6169
  function isCronAddAction(args) {
5900
6170
  const action = args.action;
5901
6171
  return typeof action === "string" && action.trim().toLowerCase() === "add";
5902
6172
  }
5903
6173
  //#endregion
5904
- 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 };
6174
+ export { shouldEnableCodexAppServerNativeToolSurface as C, mapCodexAppServerRemoteWorkspacePath as E, resolveCodexSandboxEnvironmentSelection as S, shouldWarnCodexDynamicToolBuildStageSummary as T, formatCodexDynamicToolBuildStageSummary as _, emitDynamicToolStartedDiagnostic as a, resolveCodexExternalSandboxPolicyForOpenClawSandbox as b, ensureCodexSandboxExecServerEnvironment as c, hasCodexNativeBackgroundProcesses as d, readBoundedCodexRemoteWorkspaceFile as f, disableCodexPluginThreadConfig as g, createCodexDynamicToolBuildStageTracker as h, emitDynamicToolErrorDiagnostic as i, releaseCodexSandboxExecServerEnvironment as l, buildDynamicTools as m, projectCodexExecutableDynamicTools as n, emitDynamicToolTerminalDiagnostic as o, CodexNativeToolLifecycleProjector as p, routeCodexAppServerElicitationRequest as r, handleCodexAppServerApprovalRequest as s, createCodexDynamicToolBridge as t, CodexNativeProcessAuthority as u, resolveCodexAppServerExecutionCwd as v, shouldRequireCodexSandboxExecServerEnvironment as w, resolveCodexMessageToolProvider as x, resolveCodexAppServerHookChannelId as y };