@openclaw/codex 2026.10.2 → 2026.10.5-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/.setup/{account-usage-runtime-DPXsyONK.mjs → account-usage-runtime-B6S1BQU7.mjs} +8 -7
  2. package/dist/.setup/{app-server-policy-Bg4FZw1f.mjs → app-server-policy-D_zv8J7V.mjs} +3 -4
  3. package/dist/.setup/{apply-CQ3brq3z.mjs → apply-Cc1DfWPz.mjs} +40 -51
  4. package/dist/.setup/{attempt-client-cleanup-DPUGAWP2.mjs → attempt-client-cleanup-CxX4cTSi.mjs} +6 -6
  5. package/dist/.setup/attempt-terminal-jO5IMj9K.mjs +2 -0
  6. package/dist/.setup/{attempt-workspace-context-GllQy8mw.mjs → attempt-workspace-context-CcwP3DA3.mjs} +21 -18
  7. package/dist/.setup/{auth-binding-BoMwpuPN.mjs → auth-binding-BaxlhaNx.mjs} +2 -2
  8. package/dist/.setup/{auth-cache-key-BE1HXsNo.mjs → auth-cache-key-tENCQOub.mjs} +30 -41
  9. package/dist/.setup/{binding-connection-C-08ez1w.mjs → binding-connection-B7HAAZSI.mjs} +2 -2
  10. package/dist/.setup/{binding-connection.runtime-Dn2p-fdy.mjs → binding-connection.runtime-DAXn7iFl.mjs} +6 -5
  11. package/dist/.setup/{bounded-turn-9YflG60n.mjs → bounded-turn-DxtRqV8t.mjs} +21 -17
  12. package/dist/.setup/{bounded-turn-isolation-DklXNk3k.mjs → bounded-turn-isolation-BfDAo-qZ.mjs} +4 -4
  13. package/dist/.setup/{build-state-LS-A65iX.mjs → build-state-CMYRQ_We.mjs} +1 -1
  14. package/dist/.setup/{capabilities-kQntafj_.mjs → capabilities-DbOzOglY.mjs} +1 -1
  15. package/dist/.setup/{client-catalog-response-BprQziLV.mjs → client-catalog-response-B7oqHQGO.mjs} +25 -32
  16. package/dist/.setup/{client-ChJtGEdJ.mjs → client-zrCKOu_E.mjs} +198 -247
  17. package/dist/.setup/{command-dispatch-BoC9hvFs.mjs → command-dispatch-ImiHvcyW.mjs} +3 -4
  18. package/dist/.setup/{command-formatters-DV3DHhdr.mjs → command-formatters-xW9KpoSZ.mjs} +16 -26
  19. package/dist/.setup/{command-handlers-BVX2quUD.mjs → command-handlers-B8CT4ngS.mjs} +270 -360
  20. package/dist/.setup/{command-rpc-M4gYsRiV.mjs → command-rpc-HtQjCQBg.mjs} +19 -18
  21. package/dist/.setup/{compact-D3JNLG_b.mjs → compact-Do9HPeDr.mjs} +41 -61
  22. package/dist/.setup/{computer-use-CF0jsDi1.mjs → computer-use-CeF5GuCX.mjs} +109 -147
  23. package/dist/.setup/{config-0Gxk3_eq.mjs → config-C1YVVLEA.mjs} +32 -59
  24. package/dist/.setup/config-contracts.shared-D85SGEuq.mjs +4 -0
  25. package/dist/.setup/{config-options-HG_Rr3W_.mjs → config-options-DDzeNQ8m.mjs} +52 -90
  26. package/dist/.setup/{config-parsing-BQq-CHTF.mjs → config-parsing-DsWSCPHO.mjs} +19 -55
  27. package/dist/.setup/{config-runtime-cUYrqu_a.mjs → config-runtime-DWzK-k0n.mjs} +2 -2
  28. package/dist/.setup/{config-security-KAPScztc.mjs → config-security-clxAZYWX.mjs} +35 -39
  29. package/dist/.setup/config-utils-DN_YT7dE.mjs +47 -0
  30. package/dist/.setup/{context-engine-projection-CsgYSQhW.mjs → context-engine-projection-ps-yn_ci.mjs} +10 -22
  31. package/dist/.setup/{conversation-binding-7kceEUf0.mjs → conversation-binding-D78aEdkE.mjs} +85 -119
  32. package/dist/.setup/{conversation-binding-data-DJ4ix-W5.mjs → conversation-binding-data-UgLuTSQT.mjs} +9 -8
  33. package/dist/.setup/{conversation-control-BpJpPo0t.mjs → conversation-control-GXM3unE4.mjs} +7 -10
  34. package/dist/.setup/{cyber-failover-Bb1J6hS1.mjs → cyber-failover-D4dgNfFf.mjs} +2 -5
  35. package/dist/.setup/{cyber-failover-notice-XR03ef31.mjs → cyber-failover-notice-ljgGGL0s.mjs} +1 -1
  36. package/dist/.setup/{doctor-start-options-DPW9JxIE.mjs → doctor-start-options-JjTAl-q_.mjs} +5 -5
  37. package/dist/.setup/{dynamic-tool-build-BCi00feJ.mjs → dynamic-tool-build-BMiiFROa.mjs} +57 -69
  38. package/dist/.setup/{dynamic-tools-DUv6Sr6h.mjs → dynamic-tools-DPPHrGKB.mjs} +680 -709
  39. package/dist/.setup/{effective-mcp-catalog-CIdbHp1t.mjs → effective-mcp-catalog-x3JVEcJp.mjs} +11 -20
  40. package/dist/.setup/{elicitation-input-r9axtppm.mjs → elicitation-input-DXG_qaXr.mjs} +14 -20
  41. package/dist/.setup/{ephemeral-turn-U4th15a6.mjs → ephemeral-turn-Dk0wWoqi.mjs} +3 -3
  42. package/dist/.setup/{event-projector-assistant-message-BwG5TUYW.mjs → event-projector-assistant-message-CLMKN4x-.mjs} +2 -2
  43. package/dist/.setup/{event-projector-usage-03R0I2dM.mjs → event-projector-usage-BpKfdoTm.mjs} +20 -33
  44. package/dist/.setup/{event-projector-values-DTqZHizf.mjs → event-projector-values-CLvG1Hil.mjs} +4 -5
  45. package/dist/.setup/history-rejection-B4nOXNXF.mjs +13 -0
  46. package/dist/.setup/{inference-context-D-9gWlTR.mjs → inference-context-DNlGwzwy.mjs} +10 -11
  47. package/dist/.setup/{inference-dispatch-B5UKEUyC.mjs → inference-dispatch-CO6tTs4q.mjs} +5 -4
  48. package/dist/.setup/{inference-metadata-CgIf7sOx.mjs → inference-metadata-DCvcG_N_.mjs} +36 -41
  49. package/dist/.setup/{inference-proxy-DUO5E2QQ.mjs → inference-proxy-BDWV0djM.mjs} +10 -14
  50. package/dist/.setup/{isolated-completion-C6QKyyDS.mjs → isolated-completion-z0ii1a_V.mjs} +8 -8
  51. package/dist/.setup/local-runtime-attribution-CZz2DJ4q.mjs +15 -0
  52. package/dist/.setup/{managed-binary-C1VFoVPf.mjs → managed-binary-BtIjV7yN.mjs} +16 -36
  53. package/dist/.setup/{mcp-app-session-preparation-JFCtSkRp.mjs → mcp-app-session-preparation-GTx-bRGN.mjs} +24 -25
  54. package/dist/.setup/{mcp-tool-metadata-j10HhfTC.mjs → mcp-tool-metadata-DW41snf3.mjs} +1 -1
  55. package/dist/.setup/{model-catalog-DD-evckc.mjs → model-catalog-VUdFUqnH.mjs} +8 -8
  56. package/dist/.setup/{models-DaW-7tld.mjs → models-Dph0RqpR.mjs} +6 -10
  57. package/dist/.setup/{native-auth-Cjb9y7IZ.mjs → native-auth-esN5MdIv.mjs} +5 -5
  58. package/dist/.setup/{native-execution-policy-ClzpdG3D.mjs → native-execution-policy-MXEe7PB3.mjs} +11 -28
  59. package/dist/.setup/native-hook-relay-CSZpcjwq.mjs +561 -0
  60. package/dist/.setup/{native-mcp-app-ChpyWmL-.mjs → native-mcp-app-CDrrBaTE.mjs} +6 -6
  61. package/dist/.setup/native-skill-isolation-BAatEJZb.mjs +391 -0
  62. package/dist/.setup/{native-subagent-monitor-wh_-K3Vo.mjs → native-subagent-monitor-D5O_o07p.mjs} +505 -639
  63. package/dist/.setup/{native-subagent-pending-assignments-BDqhOdJp.mjs → native-subagent-pending-assignments-DmVhedNs.mjs} +11 -23
  64. package/dist/.setup/{native-task-assignments-C6KVqiN1.mjs → native-task-assignments-DimsQ9Qa.mjs} +73 -27
  65. package/dist/.setup/{node-cli-sessions-B5oNDkUS.mjs → node-cli-sessions-CzKg2nGq.mjs} +18 -26
  66. package/dist/.setup/{node-exec-server.runtime-0xSEjKYt.mjs → node-exec-server.runtime-BWkULhBM.mjs} +5 -8
  67. package/dist/.setup/{plan-CUHSuL6_.mjs → plan-QNW5zpFp.mjs} +40 -50
  68. package/dist/.setup/{plugin-activation-BcyBv8PG.mjs → plugin-activation-D7WkJMvU.mjs} +4 -5
  69. package/dist/.setup/{plugin-app-cache-key-BW8rd98k.mjs → plugin-app-cache-key-Bg9DbDI6.mjs} +18 -23
  70. package/dist/.setup/{plugin-approval-roundtrip-bPQbG8BI.mjs → plugin-approval-roundtrip-DOQ3Tibe.mjs} +4 -6
  71. package/dist/.setup/{plugin-inventory-Bf0_piTw.mjs → plugin-inventory-CYgdo14P.mjs} +28 -43
  72. package/dist/.setup/{plugin-marketplace-discovery-SQPhSuic.mjs → plugin-marketplace-discovery-BblHM3DU.mjs} +7 -10
  73. package/dist/.setup/{plugin-metadata-cache-DWnB7VMj.mjs → plugin-metadata-cache-Bi8TAPwX.mjs} +5 -10
  74. package/dist/.setup/project-doc-thread-config-BVcvnps8.mjs +490 -0
  75. package/dist/.setup/{provider-Ct6-1lsF.mjs → provider-BG5nXBnY.mjs} +4 -4
  76. package/dist/.setup/{provider-capabilities-DhkeeHJd.mjs → provider-capabilities-DtJKxDf6.mjs} +1 -1
  77. package/dist/.setup/{rate-limits-CMPy24F5.mjs → rate-limits-CI8pTPqL.mjs} +15 -32
  78. package/dist/.setup/reasoning-effort-CS9kKWB9.mjs +37 -0
  79. package/dist/.setup/{request-CZkKkN6c.mjs → request-6piO0U_P.mjs} +28 -48
  80. package/dist/.setup/{rpc-error-hmWM21RB.mjs → rpc-error-C98SHchT.mjs} +3 -6
  81. package/dist/.setup/{run-attempt-C6-678kR.mjs → run-attempt-DejV9vDW.mjs} +927 -860
  82. package/dist/.setup/{run-attempt-lifecycle-BuZgVbDA.mjs → run-attempt-lifecycle-KN4oh-7O.mjs} +13 -4
  83. package/dist/.setup/{runtime-artifact-CUTOOnKw.mjs → runtime-artifact-C13pR7tD.mjs} +18 -22
  84. package/dist/.setup/{runtime-artifact-connection-CADz3_BY.mjs → runtime-artifact-connection-jS5DeCok.mjs} +3 -3
  85. package/dist/.setup/{sandbox-guard-WqE8yPge.mjs → sandbox-guard-DIImwCy1.mjs} +4 -6
  86. package/dist/.setup/service-tier-normalization-BPgw2Wdt.mjs +9 -0
  87. package/dist/.setup/{session-binding-Ce7BqiPN.mjs → session-binding-JEHNMPsr.mjs} +13 -40
  88. package/dist/.setup/{session-binding-record-BZB8RgcF.mjs → session-binding-record-DU_0biMD.mjs} +4 -4
  89. package/dist/.setup/{session-binding-record-codec-z4ZpDn6E.mjs → session-binding-record-codec-BpH8u_G9.mjs} +16 -39
  90. package/dist/.setup/{session-binding-sidecars-BrdV95Fg.mjs → session-binding-sidecars-DD-enC8w.mjs} +49 -54
  91. package/dist/.setup/{session-catalog-adoption-CPIP4S_y.mjs → session-catalog-adoption-Ca3OYGYM.mjs} +23 -27
  92. package/dist/.setup/{session-catalog-archive-DcgwvSbF.mjs → session-catalog-archive-Bv6gV7x9.mjs} +6 -6
  93. package/dist/.setup/{session-catalog-events-Cj-b2FpC.mjs → session-catalog-events-BziNSfLx.mjs} +6 -5
  94. package/dist/.setup/{session-catalog-index-BnvC43eZ.mjs → session-catalog-index-BmaZFGEH.mjs} +111 -105
  95. package/dist/.setup/{session-catalog-index-order-nhyO0wdW.mjs → session-catalog-index-order-CTfoefow.mjs} +28 -5
  96. package/dist/.setup/{session-catalog-limits-DEU36Qlo.mjs → session-catalog-limits-BynIoOYm.mjs} +1 -1
  97. package/dist/.setup/{session-catalog-list-request-tXephOB4.mjs → session-catalog-list-request-D4MYNgDB.mjs} +11 -8
  98. package/dist/.setup/{session-catalog-native-projection-DCtsWKSF.mjs → session-catalog-native-projection-4CsufcT8.mjs} +5 -61
  99. package/dist/.setup/{session-catalog-node-adoption-7-WEQTB1.mjs → session-catalog-node-adoption-BdP9Zjqo.mjs} +2 -2
  100. package/dist/.setup/{session-catalog-node-continue-DLWBwAAO.mjs → session-catalog-node-continue-CNJibO4_.mjs} +27 -49
  101. package/dist/.setup/{session-catalog-node-lookup-Uufckwcm.mjs → session-catalog-node-lookup-BH_DBGa9.mjs} +3 -3
  102. package/dist/.setup/{session-catalog-parsing-B-qWJh2x.mjs → session-catalog-parsing-DkeoVn60.mjs} +9 -19
  103. package/dist/.setup/{session-catalog-projection-Cn0bLb-d.mjs → session-catalog-projection-CcIdJdsd.mjs} +8 -22
  104. package/dist/.setup/session-catalog-source-CdPqS9sa.mjs +58 -0
  105. package/dist/.setup/{session-cli-kH4vuGod.mjs → session-cli-GwU0izn-.mjs} +20 -41
  106. package/dist/.setup/{session-history-BogBHKLk.mjs → session-history-BO6i_pfk.mjs} +1 -1
  107. package/dist/.setup/{session-history-import-NWKPa4ZO.mjs → session-history-import-CaO8s6ta.mjs} +4 -4
  108. package/dist/.setup/{session-history-read-CNN8zwbD.mjs → session-history-read-BI_2jRED.mjs} +4 -15
  109. package/dist/.setup/{session-history.worker-Cq_4DYgN.mjs → session-history.worker-DJQfek5p.mjs} +147 -159
  110. package/dist/.setup/{session-initialization-D6dtFQkE.mjs → session-initialization-Bi_2DKhc.mjs} +3 -5
  111. package/dist/.setup/{session-permission-policy-BxIUJ0Zd.mjs → session-permission-policy-CD2MAWU8.mjs} +12 -20
  112. package/dist/.setup/{session-retirement-Bo81t4Fb.mjs → session-retirement-CcWVhLVr.mjs} +7 -7
  113. package/dist/.setup/{settled-turn-context-BZsMjkIO.mjs → settled-turn-context-BPL6D3RO.mjs} +1 -1
  114. package/dist/.setup/{settled-turn-finalizer-CFARFqNd.mjs → settled-turn-finalizer-BezKSo-Y.mjs} +12 -11
  115. package/dist/.setup/{shared-client-5NUq43f3.mjs → shared-client-BjiqpbC5.mjs} +164 -227
  116. package/dist/.setup/{side-question-oTlegAz3.mjs → side-question-YQ5FObkD.mjs} +116 -137
  117. package/dist/.setup/{source-1cabQi3x.mjs → source-Ce3XIIsU.mjs} +37 -56
  118. package/dist/.setup/thread-archive-guard-LKdwQKyr.mjs +56 -0
  119. package/dist/.setup/{thread-lifecycle--EPPTNTe.mjs → thread-lifecycle-run-BWy0LDLB.mjs} +338 -478
  120. package/dist/.setup/thread-model-selection-DcVebr8N.mjs +61 -0
  121. package/dist/.setup/{thread-ownership-T7T_iffm.mjs → thread-ownership-5X4197vk.mjs} +5 -5
  122. package/dist/.setup/{thread-ownership-queue-Dp24d7-F.mjs → thread-ownership-queue-CeyYScUC.mjs} +5 -6
  123. package/dist/.setup/{thread-policy-VtqLex1l.mjs → thread-policy-BEecBTJM.mjs} +4 -4
  124. package/dist/.setup/thread-requests-BgzJwJtS.mjs +745 -0
  125. package/dist/.setup/{thread-resume-buZujRcC.mjs → thread-resume-BXY_6o6Z.mjs} +5 -5
  126. package/dist/.setup/{timeout-CXOfNoaX.mjs → timeout-D0mEWSGG.mjs} +6 -12
  127. package/dist/.setup/{transcript-history-projection-B9a7HwUV.mjs → transcript-history-projection-DfKcoZCB.mjs} +3 -4
  128. package/dist/.setup/{transcript-mirror-BfLRARNL.mjs → transcript-mirror-Bee2o6jj.mjs} +68 -68
  129. package/dist/.setup/{transcript-mirror-attestation-YSegaktH.mjs → transcript-mirror-attestation-BzUXnrvv.mjs} +5 -2
  130. package/dist/.setup/{transport-stdio-a0HDa2Mj.mjs → transport-stdio-CRWTkKOn.mjs} +67 -77
  131. package/dist/.setup/{transport-websocket-zqJ0Cmtn.mjs → transport-websocket-DIyPfkHo.mjs} +8 -16
  132. package/dist/.setup/{turn-router-BOKSJmfw.mjs → turn-router-D8DLkZgH.mjs} +30 -31
  133. package/dist/.setup/upstream-prompt-provenance-Ccd9MFRJ.mjs +44 -0
  134. package/dist/.setup/{upstream-session-fork-C4sbIoZb.mjs → upstream-session-fork-DQZbaYMK.mjs} +50 -43
  135. package/dist/.setup/{usage-Cq21xTMZ.mjs → usage-DVR5mNJK.mjs} +4 -4
  136. package/dist/.setup/{usage-limit-error-CGJUoQu8.mjs → usage-limit-error-BEONfgt-.mjs} +12 -21
  137. package/dist/.setup/web-search-item-CvokaZOo.mjs +20 -0
  138. package/dist/.setup/{web-search-provider.runtime-DrVE7-Br.mjs → web-search-provider.runtime-CffHOpYY.mjs} +3 -3
  139. package/dist/.setup/{workspace-write-sandbox-probe-DmUHLrrl.mjs → workspace-write-sandbox-probe-BDWO2Qkh.mjs} +4 -4
  140. package/dist/api.js +15 -15
  141. package/dist/catalog-page.worker.js +1 -1
  142. package/dist/cli-metadata.js +1 -1
  143. package/dist/doctor-contract-api.js +5 -6
  144. package/dist/harness.js +32 -61
  145. package/dist/index.js +302 -416
  146. package/dist/media-understanding-provider.js +32 -43
  147. package/dist/migration-provider-api.js +1 -1
  148. package/dist/native-session-binding-api.js +1 -1
  149. package/dist/native-tool-policy.js +23 -1
  150. package/dist/provider-discovery.js +1 -1
  151. package/dist/session-history-worker-runtime.js +52 -31
  152. package/dist/session-history.worker.js +1 -1
  153. package/openclaw.plugin.json +20 -3
  154. package/package.json +7 -7
  155. package/dist/.setup/attempt-terminal-gZrxQ35N.mjs +0 -5
  156. package/dist/.setup/plugin-thread-config-deadline-C5i1S6RN.mjs +0 -124
  157. package/dist/.setup/service-tier-normalization-Cei89SW6.mjs +0 -15
  158. package/dist/.setup/shell-dynamic-tools-Bmedt1QE.mjs +0 -85
  159. package/dist/.setup/thread-archive-guard-C9frICzX.mjs +0 -60
  160. package/dist/.setup/thread-requests-Bj_Lixmv.mjs +0 -2111
  161. package/dist/.setup/upstream-prompt-provenance-fXkKbPVC.mjs +0 -37
@@ -1,39 +1,46 @@
1
1
  import { i as isJsonObject, r as flattenCodexDynamicToolFunctions, t as CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE } from "./protocol-CANUwXJ3.mjs";
2
- import { o as formatCodexDisplayText } from "./command-formatters-DV3DHhdr.mjs";
3
- import { o as readItem } from "./event-projector-values-DTqZHizf.mjs";
4
- import { s as codexAppIdentityKey } from "./plugin-app-cache-key-BW8rd98k.mjs";
5
- import { r as invalidInlineImageText } from "./session-history-read-CNN8zwbD.mjs";
6
- import { U as protectCodexAppServerLiveThread, _ as retainSharedCodexAppServerClientIfCurrent } from "./shared-client-5NUq43f3.mjs";
7
- import { F as createFailedDynamicToolResponse, I as failedToolResult, P as resolveCodexToolAbortTerminalReason, l as CodexServerRequestResolvedError, w as createCodexElicitationResponse, x as readCodexTurnCompletedNotification } from "./client-ChJtGEdJ.mjs";
2
+ import { n as codexSandboxPolicyForTurn } from "./config-options-DDzeNQ8m.mjs";
3
+ import { o as formatCodexDisplayText } from "./command-formatters-xW9KpoSZ.mjs";
4
+ import { o as readItem } from "./event-projector-values-CLvG1Hil.mjs";
5
+ import { s as codexAppIdentityKey } from "./plugin-app-cache-key-Bg9DbDI6.mjs";
6
+ import "./config-C1YVVLEA.mjs";
7
+ import { r as invalidInlineImageText } from "./session-history-read-BI_2jRED.mjs";
8
+ import { o as neutralizeCodexExplicitMentionSigils } from "./context-engine-projection-ps-yn_ci.mjs";
9
+ import { U as protectCodexAppServerLiveThread, _ as retainSharedCodexAppServerClientIfCurrent } from "./shared-client-BjiqpbC5.mjs";
10
+ import { A as createFailedDynamicToolResponse, j as failedToolResult, k as resolveCodexToolAbortTerminalReason, l as CodexServerRequestResolvedError, w as createCodexElicitationResponse, x as readCodexTurnCompletedNotification } from "./client-zrCKOu_E.mjs";
8
11
  import { t as codexWebSocketDataToBuffer } from "./websocket-data-XlRvt8Ys.mjs";
9
- import { _ as itemStatus, a as isCodexNotificationForTurn, b as shouldClearTerminalPresentationForNativeItem, c as auditNativeToolName, g as itemName, l as auditNativeToolTerminalStatus, o as readCodexNotificationThreadId, s as readCodexNotificationTurnId, u as auditNativeToolUnfinishedStatus } from "./turn-router-BOKSJmfw.mjs";
10
- import { M as emitCodexNativePreToolUseFailureDiagnostic } from "./thread-requests-Bj_Lixmv.mjs";
11
- 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-bPQbG8BI.mjs";
12
- import { d as isCodexPassThroughMediaSource, f as mapCodexAppServerLocalWorkspacePath, p as mapCodexAppServerRemoteWorkspacePath } from "./dynamic-tool-build-BCi00feJ.mjs";
12
+ import { S as shouldClearTerminalPresentationForNativeItem, _ as itemName, a as isCodexNotificationForTurn, c as readCodexNotificationTurnId, d as auditNativeToolUnfinishedStatus, l as auditNativeToolName, s as readCodexNotificationThreadId, u as auditNativeToolTerminalStatus, v as itemStatus } from "./turn-router-D8DLkZgH.mjs";
13
+ import { n as resolveCodexAppServerReasoningEffort, t as readCodexSupportedReasoningEfforts } from "./reasoning-effort-CS9kKWB9.mjs";
14
+ import { V as joinPresentSections } from "./thread-requests-BgzJwJtS.mjs";
15
+ import { r as resolveCodexAppServerRequestModelSelection, t as CODEX_NATIVE_PERSONALITY_NONE } from "./thread-model-selection-DcVebr8N.mjs";
16
+ 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-DOQ3Tibe.mjs";
17
+ import { d as isCodexPassThroughMediaSource, f as mapCodexAppServerLocalWorkspacePath, p as mapCodexAppServerRemoteWorkspacePath } from "./dynamic-tool-build-BMiiFROa.mjs";
18
+ import { c as emitCodexNativePreToolUseFailureDiagnostic } from "./native-hook-relay-CSZpcjwq.mjs";
13
19
  import { createRequire } from "node:module";
14
- import { asNonArrayRecord, asOptionalRecord, filterStringEntries, isRecord, normalizeTrimmedStringList, readNonBlankString, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
20
+ import { asNonArrayRecord, asOptionalRecord, filterStringEntries, isRecord, normalizeOptionalString, normalizeTrimmedStringList, readNonBlankString, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
15
21
  import { createHash, randomUUID } from "node:crypto";
16
22
  import { validateJsonSchemaValue } from "openclaw/plugin-sdk/json-schema-runtime";
17
23
  import { asDateTimestampMs } from "openclaw/plugin-sdk/number-runtime";
18
24
  import { signalProcessTree, spawnTerminalPty } from "openclaw/plugin-sdk/process-runtime";
25
+ import { DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS, estimateToolResultTextChars, resolveLiveToolResultMaxChars, safeParseJson, sliceToolResultTextToBudget, sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
19
26
  import path, { posix } from "node:path";
20
27
  import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
21
28
  import { createBoundedRemoteFileReader, isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
22
29
  import { once } from "node:events";
23
30
  import { spawn } from "node:child_process";
24
31
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
25
- import { DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS, estimateToolResultTextChars, resolveLiveToolResultMaxChars, safeParseJson, sliceToolResultTextToBudget, sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
26
- import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
32
+ import { emitTrustedDiagnosticEvent, hasPendingInternalDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
27
33
  import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
28
- import { createAgentToolResultMiddlewareRunner, createCodexAppServerToolResultExtensionRunner, embeddedAgentLog, extractMessagingToolSend, extractMessagingToolSendResult, finalizeAgentToolAvailability, finalizeToolTerminalPresentation, formatApprovalDisplayPath, formatToolExecutionErrorMessage, getBeforeToolCallFailureDisposition, getChannelAgentToolMeta, getPluginToolMeta, getPluginToolSideEffectOwnerKey, hasNativeHookRelayInvocation, invokeNativeHookRelay, isDeliveredMessageToolOnlySourceReplyResult, isDeliveredMessagingToolResult, isDeliveredMessagingToolSendToCurrentSource, isMessagingTool, isReplaySafeToolCall, isToolResultError, isToolWrappedWithBeforeToolCallHook, projectPluginMessageDeliveryFact, projectRuntimeToolInputSchema, readEmbeddedMessageDeliveryFact, readToolOperatorHint, resolveNativeHookRelayDeferredToolApproval, resolveToolExecutionErrorKind, runAgentHarnessAfterToolCallHook, sanitizeToolResult, setBeforeToolCallDiagnosticsEnabled, wrapToolWithBeforeToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
34
+ import { raceWithTimeout } from "openclaw/plugin-sdk/time-runtime";
29
35
  import { fileURLToPath, pathToFileURL } from "node:url";
36
+ import { buildHarnessVisibleReplyGuidance, buildTemporalContextText, captureToolAuthoredSourceReply, createAgentToolResultMiddlewareRunner, createCodexAppServerToolResultExtensionRunner, embeddedAgentLog, extractMessagingToolSend, extractMessagingToolSendResult, finalizeAgentToolAvailability, finalizeToolTerminalPresentation, formatApprovalDisplayPath, formatToolExecutionErrorMessage, getBeforeToolCallFailureDisposition, getChannelAgentToolMeta, getPluginToolMeta, getPluginToolSideEffectOwnerKey, hasNativeHookRelayInvocation, invokeNativeHookRelay, isDeliveredMessageToolOnlySourceReplyResult, isDeliveredMessagingToolResult, isDeliveredMessagingToolSendToCurrentSource, isMessagingTool, isReplaySafeToolCall, isToolResultError, isToolWrappedWithBeforeToolCallHook, projectPluginMessageDeliveryFact, projectRuntimeToolInputSchema, readEmbeddedMessageDeliveryFact, readToolOperatorHint, resolveNativeHookRelayDeferredToolApproval, resolveToolExecutionErrorKind, runAgentHarnessAfterToolCallHook, sanitizeToolResult, setBeforeToolCallDiagnosticsEnabled, wrapToolWithBeforeToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
30
37
  import { sanitizeInlineImageDataUrl } from "openclaw/plugin-sdk/inline-image-data-url-runtime";
31
38
  import { redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
32
39
  import { formatErrorMessage as formatErrorMessage$2, redactSensitiveText as redactSensitiveText$1 } from "openclaw/plugin-sdk/security-runtime";
33
40
  import { collectAgentHarnessMessagingMediaUrls, copyInternalToolResultState, createAgentHarnessToolExecutionBoundaryRegistry, extractMessagingToolSourceReplyPayload, getCoreTtsToolResultMediaUrls, mapAgentHarnessMessagingMediaValues, normalizeAcceptedSessionSpawnResult, recordAgentHarnessToolResultTelemetry, runAgentHarnessToolInvocation } from "openclaw/plugin-sdk/agent-harness-tool-runtime";
34
41
  import { isIP } from "node:net";
35
- import { formatMcpCodexApprovalRemedy, requiresMcpCodexToolApproval, resolveProjectedMcpCodexToolApprovalMode } from "openclaw/plugin-sdk/codex-mcp-projection";
36
42
  import { SANDBOX_COMMAND_MAX_BUFFER_BYTES, buildRemoteCommand, prepareSandboxProcessCleanup, sanitizeEnvVars } from "openclaw/plugin-sdk/sandbox";
43
+ import { formatMcpCodexApprovalRemedy, requiresMcpCodexToolApproval, resolveProjectedMcpCodexToolApprovalMode } from "openclaw/plugin-sdk/codex-mcp-projection";
37
44
  import { SsrFBlockedError, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
38
45
  import { getMediaDir } from "openclaw/plugin-sdk/media-runtime";
39
46
  import { normalizeMediaReferenceForComparison, saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
@@ -86,10 +93,10 @@ var CodexNativeToolLifecycleProjector = class {
86
93
  if (candidate) return;
87
94
  candidate = mcpToolCall;
88
95
  }
89
- if (!candidate || candidate.status !== "inProgress" || typeof candidate.server !== "string" || !candidate.server.trim() || typeof candidate.tool !== "string" || !candidate.tool.trim() || candidate.arguments === void 0 || candidate.appContext != null || candidate.pluginId != null) return;
96
+ if (!candidate || candidate.status !== "inProgress" || !serverName.trim() || typeof candidate.tool !== "string" || !candidate.tool.trim() || candidate.arguments === void 0 || candidate.appContext != null || candidate.pluginId != null) return;
90
97
  return {
91
98
  id: candidate.id,
92
- server: candidate.server,
99
+ server: serverName,
93
100
  tool: candidate.tool,
94
101
  arguments: candidate.arguments
95
102
  };
@@ -410,6 +417,55 @@ async function assertGatewayManagedMediaPath(value, mediaRoot) {
410
417
  }
411
418
  }
412
419
  //#endregion
420
+ //#region extensions/codex/src/app-server/computer-context.ts
421
+ function invalidateCodexComputerFrame(contextEpoch) {
422
+ contextEpoch.value += 1;
423
+ delete contextEpoch.frameToolCallId;
424
+ delete contextEpoch.frameImageIdentity;
425
+ }
426
+ //#endregion
427
+ //#region extensions/codex/src/app-server/user-input.ts
428
+ /** Builds ordered Codex user input for both new turns and same-turn steering. */
429
+ function buildCodexUserInput(text, images, contextImageGroups, historyProvenancePrefix) {
430
+ if (text !== void 0 && contextImageGroups?.length) {
431
+ let offset = 0;
432
+ return prependHistoryProvenance([...contextImageGroups.flatMap((group) => {
433
+ const parts = buildCodexUserInput(text.slice(offset, group.end), group.images);
434
+ offset = group.end;
435
+ return parts;
436
+ }), ...buildCodexUserInput(text.slice(offset), images)]);
437
+ }
438
+ const imageInputs = (images ?? []).map((image) => {
439
+ const imageUrl = sanitizeInlineImageDataUrl(`data:${image.mimeType};base64,${image.data}`);
440
+ return imageUrl ? {
441
+ type: "image",
442
+ url: imageUrl
443
+ } : {
444
+ type: "text",
445
+ text: invalidInlineImageText("codex user input"),
446
+ text_elements: []
447
+ };
448
+ });
449
+ return prependHistoryProvenance([...text === void 0 ? [] : [{
450
+ type: "text",
451
+ text,
452
+ text_elements: []
453
+ }], ...imageInputs]);
454
+ function prependHistoryProvenance(input) {
455
+ if (!historyProvenancePrefix) return input;
456
+ const firstTextIndex = input.findIndex((item) => item.type === "text");
457
+ if (firstTextIndex === -1) return [{
458
+ type: "text",
459
+ text: historyProvenancePrefix,
460
+ text_elements: []
461
+ }, ...input];
462
+ return input.map((item, index) => index === firstTextIndex && item.type === "text" ? {
463
+ ...item,
464
+ text: `${historyProvenancePrefix}${item.text}`
465
+ } : item);
466
+ }
467
+ }
468
+ //#endregion
413
469
  //#region extensions/codex/src/app-server/native-process-authority.ts
414
470
  const clients = /* @__PURE__ */ new WeakMap();
415
471
  /** A physical client joins native call receipts to its concrete sandbox processes. */
@@ -524,17 +580,14 @@ var CodexNativeProcessClient = class {
524
580
  assertAdmission();
525
581
  const process = { terminate };
526
582
  command.processes.add(process);
527
- let settled = false;
528
583
  return {
529
584
  assertAdmission,
530
585
  assertCurrent: () => {
531
586
  command.owner.assertCurrent();
532
- if (this.closed || settled) throw new Error("Codex native process authority has ended");
587
+ if (this.closed || !command.processes.has(process)) throw new Error("Codex native process authority has ended");
533
588
  },
534
589
  settle: () => {
535
- if (settled) return;
536
- settled = true;
537
- command.processes.delete(process);
590
+ if (!command.processes.delete(process)) return;
538
591
  this.forgetSettled(command);
539
592
  },
540
593
  fail: (error) => command.owner.reportSettlementFailure(error)
@@ -689,16 +742,133 @@ var CodexNativeProcessAuthority = class {
689
742
  }
690
743
  };
691
744
  //#endregion
745
+ //#region extensions/codex/src/app-server/turn-params.ts
746
+ const CODEX_CURRENT_SENDER_FIELD_MAX_CHARS = 256;
747
+ function readCodexCurrentSender(params) {
748
+ const metadata = asOptionalRecord(asOptionalRecord(params.userTurnTranscriptRecorder?.message)?.["__openclaw"]);
749
+ const fields = [
750
+ "senderId",
751
+ "senderName",
752
+ "senderUsername"
753
+ ];
754
+ const recorded = fields.map((key) => normalizeOptionalString(metadata?.[key]));
755
+ const [id, name, username] = recorded.some(Boolean) ? recorded : fields.map((key) => normalizeOptionalString(params[key]));
756
+ if (!id && !name && !username) return;
757
+ const bound = (value) => truncateUtf16Safe(value, CODEX_CURRENT_SENDER_FIELD_MAX_CHARS);
758
+ return {
759
+ ...id ? { id: bound(id) } : {},
760
+ ...name ? { name: bound(name) } : {},
761
+ ...username ? { username: bound(username) } : {}
762
+ };
763
+ }
764
+ function buildCodexHistoryProvenancePrefix(params) {
765
+ const sender = readCodexCurrentSender(params);
766
+ return sender?.id ? neutralizeCodexExplicitMentionSigils(`[OpenClaw conversation info: sender=${JSON.stringify(sender)}]\n`) : void 0;
767
+ }
768
+ function buildTurnStartParams(params, options) {
769
+ const modelSelection = options.preserveNativeTurnSettings ? void 0 : resolveCodexAppServerRequestModelSelection({
770
+ homeScope: options.appServer.start.homeScope,
771
+ model: options.model ?? params.modelId,
772
+ modelProvider: options.modelProvider,
773
+ authProfileId: params.authProfileId,
774
+ authProfileStore: params.authProfileStore,
775
+ agentDir: params.agentDir,
776
+ config: params.config
777
+ });
778
+ const collaborationMode = modelSelection ? buildTurnCollaborationMode(params, { model: modelSelection.model }) : void 0;
779
+ if (collaborationMode && options.parentLocalEgress) collaborationMode.settings.developer_instructions = null;
780
+ const useThreadPermissionProfile = options.appServer.networkProxy && !options.sandboxPolicy;
781
+ const currentSender = params.trigger === "user" ? readCodexCurrentSender(params) : void 0;
782
+ const additionalContext = {
783
+ ...buildCodexTemporalAdditionalContext(params, { sessionStatusAvailable: options.sessionStatusAvailable === true }),
784
+ openclaw_active_computer: {
785
+ kind: "application",
786
+ value: params.hostCapabilities.activeComputerContext?.() ?? "Current active computer: active_node=unknown (host presence unavailable)"
787
+ },
788
+ openclaw_source_delivery: {
789
+ kind: "application",
790
+ value: ["Current source-delivery policy for this turn (replaces earlier source-delivery guidance):", buildHarnessVisibleReplyGuidance({
791
+ sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
792
+ messageToolAvailable: options.messageToolAvailable === true,
793
+ requireExplicitMessageTarget: options.requireExplicitMessageTarget
794
+ })].join("\n")
795
+ }
796
+ };
797
+ if (currentSender) additionalContext.openclaw_current_sender = {
798
+ kind: "untrusted",
799
+ value: JSON.stringify({ sender: currentSender })
800
+ };
801
+ if (params.permissionChange?.notice) additionalContext.openclaw_permission_change = {
802
+ kind: "application",
803
+ value: params.permissionChange.notice
804
+ };
805
+ return {
806
+ threadId: options.threadId,
807
+ ...params.trigger ? { turnTrigger: params.trigger } : {},
808
+ input: [...buildCodexUserInput(options.promptText ?? params.prompt, params.images, options.contextImageGroups, options.historyProvenancePrefix ?? (params.trigger === "user" ? buildCodexHistoryProvenancePrefix(params) : void 0)), ...options.explicitSkillInputs ?? []],
809
+ additionalContext,
810
+ cwd: options.cwd,
811
+ ...options.appServer.sessionRoot ? { runtimeWorkspaceRoots: [options.appServer.sessionRoot] } : {},
812
+ approvalPolicy: options.appServer.approvalPolicy,
813
+ approvalsReviewer: options.appServer.approvalsReviewer,
814
+ ...useThreadPermissionProfile ? {} : { sandboxPolicy: options.sandboxPolicy ?? codexSandboxPolicyForTurn(options.appServer.sandbox, options.appServer.sessionRoot ?? options.cwd, options.appServer.start?.args) },
815
+ ...modelSelection ? {
816
+ model: modelSelection.model,
817
+ personality: CODEX_NATIVE_PERSONALITY_NONE
818
+ } : {},
819
+ ...options.appServer.serviceTier !== void 0 ? { serviceTier: options.appServer.serviceTier } : options.clearInheritedServiceTier ? { serviceTier: null } : {},
820
+ ...collaborationMode ? {
821
+ effort: collaborationMode.settings.reasoning_effort,
822
+ collaborationMode
823
+ } : {},
824
+ ...params.requireWorkspaceOnly === true ? { environments: [] } : options.environmentSelection ? { environments: options.environmentSelection } : {}
825
+ };
826
+ }
827
+ function buildCodexTemporalAdditionalContext(params, options) {
828
+ return { openclaw_temporal_context: {
829
+ kind: "application",
830
+ value: buildTemporalContextText({
831
+ configuredTimezone: params.config?.agents?.defaults?.userTimezone,
832
+ sessionStatusAvailable: options.sessionStatusAvailable
833
+ })
834
+ } };
835
+ }
836
+ function buildTurnCollaborationMode(params, options = {}) {
837
+ const model = options.model ?? params.modelId;
838
+ return {
839
+ mode: "default",
840
+ settings: {
841
+ model,
842
+ reasoning_effort: resolveCodexAppServerReasoningEffort({
843
+ thinkLevel: params.thinkLevel,
844
+ modelId: model,
845
+ supportedReasoningEfforts: readCodexSupportedReasoningEfforts(params.model?.compat)
846
+ }),
847
+ developer_instructions: params.trigger === "cron" ? CRON_COLLABORATION_INSTRUCTIONS : null
848
+ }
849
+ };
850
+ }
851
+ function buildCodexParentLocalInstructions(params, options = {}) {
852
+ const contextInstructions = joinPresentSections(options.personaInstructions, options.skillsInstructions, options.memoryInstructions);
853
+ if (params.trigger === "cron") return joinPresentSections(CRON_COLLABORATION_INSTRUCTIONS, contextInstructions);
854
+ return contextInstructions || null;
855
+ }
856
+ const CRON_COLLABORATION_INSTRUCTIONS = [
857
+ "This is an OpenClaw cron automation turn. Apply these instructions only to this scheduled job; ordinary chat turns should stay in Codex Default mode.",
858
+ "Execute the cron payload directly. If it asks you to run an exact command, run that command before doing any investigation, planning, memory review, or workspace bootstrap.",
859
+ "Use context already provided by the runtime, but do not spend time loading or re-reading workspace bootstrap, memory, or project-doc files before executing the cron payload. Inspect those files only if the payload asks for them or the command fails and they are needed to diagnose it.",
860
+ "Keep output concise and automation-oriented. Prefer the final command result or a short failure summary over status narration."
861
+ ].join("\n\n");
862
+ //#endregion
692
863
  //#region extensions/codex/src/app-server/native-pre-tool-use-failures.ts
693
864
  /** Holds failures until a projector or terminal diagnostic owns their delivery. */
694
865
  function createCodexNativePreToolUseFailureBuffer(params) {
695
866
  const pending = [];
696
- let active = false;
697
- let terminalReason;
867
+ let fallback;
698
868
  const emit = (failure) => emitCodexNativePreToolUseFailureDiagnostic({
699
869
  ...params,
700
870
  failure,
701
- ...active ? { terminalReason: terminalReason ?? failure.disposition } : {}
871
+ ...fallback ? { terminalReason: fallback.terminalReason ?? failure.disposition } : {}
702
872
  });
703
873
  const flush = () => {
704
874
  for (const failure of pending.splice(0)) emit(failure);
@@ -706,17 +876,14 @@ function createCodexNativePreToolUseFailureBuffer(params) {
706
876
  return {
707
877
  pending,
708
878
  get active() {
709
- return active;
879
+ return fallback !== void 0;
710
880
  },
711
881
  record(failure) {
712
- if (active) emit(failure);
882
+ if (fallback) emit(failure);
713
883
  else pending.push(failure);
714
884
  },
715
885
  activateFallback(wasAborted) {
716
- if (!active) {
717
- terminalReason = wasAborted ? resolveCodexToolAbortTerminalReason(params.signal) : void 0;
718
- active = true;
719
- }
886
+ fallback ??= { terminalReason: wasAborted ? resolveCodexToolAbortTerminalReason(params.signal) : void 0 };
720
887
  flush();
721
888
  },
722
889
  flush
@@ -1071,11 +1238,6 @@ function requireString(value, label) {
1071
1238
  if (typeof value !== "string" || !value) throw new Error(`${label} must be a non-empty string.`);
1072
1239
  return value;
1073
1240
  }
1074
- /** Validates a base64 payload parameter as a string; decoding happens at call sites. */
1075
- function requireBase64String(value, label) {
1076
- if (typeof value !== "string") throw new Error(`${label} must be a string.`);
1077
- return value;
1078
- }
1079
1241
  function requireNumber(value, label) {
1080
1242
  if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`${label} must be a finite number.`);
1081
1243
  return value;
@@ -1118,34 +1280,28 @@ function sendError(send, id, code, message) {
1118
1280
  const WINDOWS_DRIVE_PATH_RE = /^\/[A-Za-z]:(?:\/|$)/u;
1119
1281
  /** Resolves one Codex exec-server PathUri into a POSIX sandbox path. */
1120
1282
  function resolveExecServerPath(rawPath, label) {
1121
- let pathUrl;
1122
- try {
1123
- pathUrl = new URL(rawPath);
1124
- } catch (error) {
1125
- throw new Error(`${label} must be a valid file URI: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
1126
- }
1283
+ const pathUrl = parseSandboxPath(() => new URL(rawPath), `${label} must be a valid file URI`);
1127
1284
  if (pathUrl.protocol !== "file:") throw new Error(`${label} URI must use the file scheme, received ${pathUrl.protocol.slice(0, -1)}.`);
1128
1285
  if (pathUrl.search || pathUrl.hash) throw new Error(`${label} file URI must not include a query or fragment.`);
1129
- let resolved;
1130
- try {
1131
- resolved = fileURLToPath(pathUrl, { windows: false });
1132
- } catch (error) {
1133
- throw new Error(`${label} file URI is not valid for the sandbox: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
1134
- }
1286
+ const resolved = parseSandboxPath(() => fileURLToPath(pathUrl, { windows: false }), `${label} file URI is not valid for the sandbox`);
1135
1287
  if (WINDOWS_DRIVE_PATH_RE.test(resolved)) throw new Error(`${label} Windows file URI is not supported by the sandbox.`);
1136
1288
  if (resolved.includes("\0")) throw new Error(`${label} file URI must not contain a null byte.`);
1137
1289
  return resolved;
1138
1290
  }
1291
+ function parseSandboxPath(parse, message) {
1292
+ try {
1293
+ return parse();
1294
+ } catch (error) {
1295
+ throw new Error(`${message}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
1296
+ }
1297
+ }
1139
1298
  //#endregion
1140
1299
  //#region extensions/codex/src/app-server/sandbox-exec-server/fs-policy.ts
1141
- /**
1142
- * Resolves Codex filesystem sandbox policy payloads into OpenClaw path/glob
1143
- * checks for sandbox exec-server filesystem operations.
1144
- */
1145
- /** Resolves request-local sandbox policy and asserts each requested path has the needed access. */
1146
- function assertFsSandboxAccess(execServer, record, requests) {
1147
- assertResolvedFsSandboxAccess(resolveFsSandboxPolicy(execServer, record), requests);
1148
- }
1300
+ const FS_ACCESS_RANK = {
1301
+ read: 0,
1302
+ write: 1,
1303
+ none: 2
1304
+ };
1149
1305
  /** Parses a Codex managed filesystem sandbox context into normalized access entries. */
1150
1306
  function resolveFsSandboxPolicy(execServer, record) {
1151
1307
  if (record.sandbox === void 0 || record.sandbox === null) return;
@@ -1182,13 +1338,8 @@ function resolveFsSandboxEntry(entry, cwd) {
1182
1338
  const access = readFsAccessMode(entry.access);
1183
1339
  const pathSpec = requireObject(entry.path, "fs sandbox entry path");
1184
1340
  const pathType = requireString(pathSpec.type, "fs sandbox entry path type");
1185
- if (pathType === "path") return {
1186
- kind: "path",
1187
- path: normalizeSandboxAbsolutePath(resolveExecServerPath(requireString(pathSpec.path, "fs sandbox path"), "fs sandbox path"), "fs sandbox path"),
1188
- access
1189
- };
1190
- if (pathType === "special") {
1191
- const path = resolveFsSpecialPath(requireObject(pathSpec.value, "fs sandbox special path"), cwd);
1341
+ if (pathType === "path" || pathType === "special") {
1342
+ const path = pathType === "path" ? normalizeSandboxAbsolutePath(resolveExecServerPath(requireString(pathSpec.path, "fs sandbox path"), "fs sandbox path"), "fs sandbox path") : resolveFsSpecialPath(requireObject(pathSpec.value, "fs sandbox special path"), cwd);
1192
1343
  return path === void 0 ? void 0 : {
1193
1344
  kind: "path",
1194
1345
  path,
@@ -1228,18 +1379,17 @@ function resolveFsSpecialPath(value, cwd) {
1228
1379
  function assertResolvedFsSandboxAccess(policy, requests) {
1229
1380
  if (!policy?.unrestricted && policy) for (const request of requests) {
1230
1381
  const access = resolveFsAccess(policy, request.path);
1231
- if (request.access === "read" && access === "none") throw new Error(`Codex fs sandbox denied read access to ${request.path}`);
1232
- if (request.access === "write" && access !== "write") throw new Error(`Codex fs sandbox denied write access to ${request.path}`);
1382
+ if (request.access === "read" ? access === "none" : access !== "write") throw new Error(`Codex fs sandbox denied ${request.access} access to ${request.path}`);
1233
1383
  }
1234
1384
  }
1235
1385
  function resolveFsAccess(policy, rawPath) {
1236
1386
  const target = normalizeSandboxAbsolutePath(rawPath, "fs path");
1237
1387
  let selected;
1238
1388
  for (const entry of policy.entries) {
1239
- if (!fsSandboxEntryMatches(entry, target)) continue;
1389
+ if (!(entry.kind === "path" ? pathContains(entry.path, target) : entry.matcher.test(target))) continue;
1240
1390
  const candidate = {
1241
- specificity: fsSandboxEntrySpecificity(entry),
1242
- rank: fsAccessRank(entry.access),
1391
+ specificity: (entry.kind === "path" ? entry.path : entry.literalPrefix).split("/").filter(Boolean).length,
1392
+ rank: FS_ACCESS_RANK[entry.access],
1243
1393
  access: entry.access
1244
1394
  };
1245
1395
  if (!selected || candidate.specificity > selected.specificity || candidate.specificity === selected.specificity && candidate.rank > selected.rank) selected = candidate;
@@ -1251,10 +1401,10 @@ function assertNoReadOnlyDescendant(policy, rawPath, operation) {
1251
1401
  if (!policy || policy.unrestricted) return;
1252
1402
  const target = normalizeSandboxAbsolutePath(rawPath, "fs path");
1253
1403
  const protectedDescendant = policy.entries.find((entry) => {
1254
- if (entry.access === "write" || !fsSandboxEntryCanAffectDescendant(entry, target)) return false;
1255
- if (entry.kind === "glob") return true;
1404
+ if (entry.access === "write") return false;
1405
+ if (entry.kind === "glob") return pathContains(target, entry.literalPrefix) || pathContains(entry.literalPrefix, target);
1256
1406
  const protectedPath = entry.path;
1257
- return protectedPath && resolveFsAccess(policy, protectedPath) !== "write";
1407
+ return pathContains(target, protectedPath) && target !== protectedPath && protectedPath && resolveFsAccess(policy, protectedPath) !== "write";
1258
1408
  });
1259
1409
  if (protectedDescendant) {
1260
1410
  const protectedPath = protectedDescendant.kind === "path" ? protectedDescendant.path : protectedDescendant.pattern;
@@ -1264,32 +1414,11 @@ function assertNoReadOnlyDescendant(policy, rawPath, operation) {
1264
1414
  /** Normalizes and validates an absolute POSIX path inside the sandbox namespace. */
1265
1415
  function normalizeSandboxAbsolutePath(rawPath, label) {
1266
1416
  if (!rawPath || rawPath.includes("\0") || !rawPath.startsWith("/")) throw new Error(`${label} must be an absolute sandbox path.`);
1267
- const normalized = posix.normalize(rawPath);
1268
- return normalized === "//" ? "/" : normalized;
1417
+ return posix.normalize(rawPath);
1269
1418
  }
1270
- /** Returns true when target is root itself or a descendant of root. */
1271
1419
  function pathContains(root, target) {
1272
1420
  return root === "/" || target === root || target.startsWith(`${root}/`);
1273
1421
  }
1274
- function fsSandboxEntryMatches(entry, target) {
1275
- if (entry.kind === "path") return pathContains(entry.path, target);
1276
- return entry.matcher.test(target);
1277
- }
1278
- function fsSandboxEntryCanAffectDescendant(entry, target) {
1279
- if (entry.kind === "path") return pathContains(target, entry.path) && target !== entry.path;
1280
- return pathContains(target, entry.literalPrefix) || pathContains(entry.literalPrefix, target);
1281
- }
1282
- function fsSandboxEntrySpecificity(entry) {
1283
- return pathSpecificity(entry.kind === "path" ? entry.path : entry.literalPrefix);
1284
- }
1285
- function pathSpecificity(filePath) {
1286
- return filePath === "/" ? 0 : filePath.split("/").filter(Boolean).length;
1287
- }
1288
- function fsAccessRank(access) {
1289
- if (access === "none") return 2;
1290
- if (access === "write") return 1;
1291
- return 0;
1292
- }
1293
1422
  function normalizeSandboxGlobPattern(pattern) {
1294
1423
  if (!pattern || pattern.includes("\0") || !pattern.startsWith("/")) throw new Error("fs sandbox glob pattern must be absolute.");
1295
1424
  return pattern.replace(/\/{2,}/gu, "/");
@@ -1329,15 +1458,10 @@ function compileSandboxGlobCharacterClass(pattern, startIndex) {
1329
1458
  endIndex: index
1330
1459
  };
1331
1460
  if (!char || char === "/") throw new Error("fs sandbox glob character class cannot match path separators.");
1332
- body += escapeSandboxGlobCharacterClassChar(char, body.length === 0);
1461
+ body += char === "\\" || char === "]" || body.length === 0 && char === "^" ? `\\${char}` : char;
1333
1462
  }
1334
1463
  throw new Error("fs sandbox glob character class must be closed.");
1335
1464
  }
1336
- function escapeSandboxGlobCharacterClassChar(char, first) {
1337
- if (char === "\\" || char === "]") return `\\${char}`;
1338
- if (first && char === "^") return "\\^";
1339
- return char;
1340
- }
1341
1465
  function sandboxGlobLiteralPrefix(pattern) {
1342
1466
  const wildcardIndex = pattern.search(/[*?[]/u);
1343
1467
  const prefix = wildcardIndex === -1 ? pattern : pattern.slice(0, wildcardIndex);
@@ -1377,6 +1501,9 @@ async function openFile(execServer, handles, params) {
1377
1501
  reservedBytes: 0
1378
1502
  };
1379
1503
  handles.set(handleId, handle);
1504
+ const assertCurrent = () => {
1505
+ if (handles.get(handleId) !== handle || handle.closeRequested || handles.closed) throw new JsonRpcProtocolError(JSON_RPC_NOT_FOUND, `unknown file read handle \`${handleId}\``);
1506
+ };
1380
1507
  try {
1381
1508
  const readPolicy = await authorizePhysicalReadPath(fsBridge, fsSandboxPolicy, filePath, handle.abortController.signal);
1382
1509
  const stat = await fsBridge.stat({
@@ -1384,7 +1511,7 @@ async function openFile(execServer, handles, params) {
1384
1511
  signal: handle.abortController.signal,
1385
1512
  ...readPolicy
1386
1513
  });
1387
- if (handles.get(handleId) !== handle || handle.closeRequested || handles.closed) throw new JsonRpcProtocolError(JSON_RPC_NOT_FOUND, `unknown file read handle \`${handleId}\``);
1514
+ assertCurrent();
1388
1515
  if (!stat) throw new JsonRpcProtocolError(JSON_RPC_NOT_FOUND, "file not found");
1389
1516
  if (stat.type !== "file") throw new JsonRpcProtocolError(-32600, "file read handle requires a regular file");
1390
1517
  if (!Number.isSafeInteger(stat.size) || stat.size < 0) throw new JsonRpcProtocolError(-32600, "file size must be a non-negative safe integer");
@@ -1396,7 +1523,7 @@ async function openFile(execServer, handles, params) {
1396
1523
  signal: handle.abortController.signal,
1397
1524
  ...readPolicy
1398
1525
  });
1399
- if (handles.get(handleId) !== handle || handle.closeRequested || handles.closed) throw new JsonRpcProtocolError(JSON_RPC_NOT_FOUND, `unknown file read handle \`${handleId}\``);
1526
+ assertCurrent();
1400
1527
  if (data.byteLength > handle.reservedBytes) throw new JsonRpcProtocolError(-32600, "sandbox file read exceeds the per-connection buffered file limit");
1401
1528
  handle.reservedBytes = data.byteLength;
1402
1529
  handle.data = data;
@@ -1452,9 +1579,10 @@ function requireFileReadHandleId(value) {
1452
1579
  if (Buffer.byteLength(handleId, "utf8") > CODEX_SANDBOX_EXEC_SERVER_MAX_FILE_READ_HANDLE_ID_BYTES) throw new JsonRpcProtocolError(-32600, `file read handle ID must not exceed ${CODEX_SANDBOX_EXEC_SERVER_MAX_FILE_READ_HANDLE_ID_BYTES} bytes`);
1453
1580
  return handleId;
1454
1581
  }
1455
- async function readFile(execServer, params) {
1456
- const record = requireObject(params, "fs/readFile params");
1457
- const filePath = resolveExecServerPath(requireString(record.path, "path"), "read path");
1582
+ async function readFileOrMetadata(execServer, params, method) {
1583
+ const record = requireObject(params, `${method} params`);
1584
+ const metadataOnly = method === "fs/getMetadata";
1585
+ const filePath = resolveExecServerPath(requireString(record.path, "path"), metadataOnly ? "metadata path" : "read path");
1458
1586
  const fsSandboxPolicy = resolveFsSandboxPolicy(execServer, record);
1459
1587
  assertResolvedFsSandboxAccess(fsSandboxPolicy, [{
1460
1588
  path: filePath,
@@ -1462,11 +1590,19 @@ async function readFile(execServer, params) {
1462
1590
  }]);
1463
1591
  const fsBridge = execServer.fsBridge;
1464
1592
  const readPolicy = await authorizePhysicalReadPath(fsBridge, fsSandboxPolicy, filePath);
1465
- const stat = await fsBridge.stat({
1593
+ const stat = await (metadataOnly ? execServer.fsBridge : fsBridge).stat({
1466
1594
  filePath,
1467
1595
  ...readPolicy
1468
1596
  });
1469
1597
  if (!stat) throw new JsonRpcProtocolError(JSON_RPC_NOT_FOUND, "file not found");
1598
+ if (metadataOnly) return {
1599
+ isDirectory: stat.type === "directory",
1600
+ isFile: stat.type === "file",
1601
+ isSymlink: false,
1602
+ size: stat.size,
1603
+ createdAtMs: 0,
1604
+ modifiedAtMs: stat.mtimeMs ?? 0
1605
+ };
1470
1606
  assertSandboxFileReadWithinLimit(stat);
1471
1607
  return { dataBase64: (await fsBridge.readFile({
1472
1608
  filePath,
@@ -1474,75 +1610,39 @@ async function readFile(execServer, params) {
1474
1610
  ...readPolicy
1475
1611
  })).toString("base64") };
1476
1612
  }
1477
- async function writeFile(execServer, params) {
1478
- const record = requireObject(params, "fs/writeFile params");
1479
- const filePath = resolveExecServerPath(requireString(record.path, "path"), "write path");
1613
+ async function writeFileOrDirectory(execServer, params, method) {
1614
+ const record = requireObject(params, `${method} params`);
1615
+ const directory = method === "fs/createDirectory";
1616
+ const filePath = resolveExecServerPath(requireString(record.path, "path"), directory ? "create-directory path" : "write path");
1480
1617
  const fsBridge = execServer.fsBridge;
1481
1618
  const canonicalDestination = await fsBridge.resolvePinnedMutationTarget?.({
1482
1619
  filePath,
1483
- action: "write"
1620
+ action: directory ? "mkdir" : "write"
1484
1621
  });
1485
- assertFsSandboxAccess(execServer, record, [{
1622
+ assertResolvedFsSandboxAccess(resolveFsSandboxPolicy(execServer, record), [{
1486
1623
  path: filePath,
1487
1624
  access: "write"
1488
1625
  }, ...canonicalDestination ? [{
1489
1626
  path: canonicalDestination.policyPath,
1490
1627
  access: "write"
1491
1628
  }] : []]);
1492
- if ((await fsBridge.stat({ filePath: posix.dirname(filePath) }))?.type !== "directory") throw new JsonRpcProtocolError(JSON_RPC_NOT_FOUND, "parent directory not found");
1493
- await fsBridge.writeFile({
1629
+ if (!directory || record.recursive === false) {
1630
+ if ((await fsBridge.stat({ filePath: posix.dirname(filePath) }))?.type !== "directory") throw new JsonRpcProtocolError(JSON_RPC_NOT_FOUND, "parent directory not found");
1631
+ }
1632
+ if (directory) await fsBridge.mkdirp({
1494
1633
  filePath,
1495
- data: Buffer.from(requireBase64String(record.dataBase64, "dataBase64"), "base64"),
1496
- mkdir: false,
1497
1634
  pinnedPath: canonicalDestination?.pinnedPath
1498
1635
  });
1499
- }
1500
- async function createDirectory(execServer, params) {
1501
- const record = requireObject(params, "fs/createDirectory params");
1502
- const filePath = resolveExecServerPath(requireString(record.path, "path"), "create-directory path");
1503
- const fsBridge = execServer.fsBridge;
1504
- const canonicalDestination = await fsBridge.resolvePinnedMutationTarget?.({
1505
- filePath,
1506
- action: "mkdir"
1507
- });
1508
- assertFsSandboxAccess(execServer, record, [{
1509
- path: filePath,
1510
- access: "write"
1511
- }, ...canonicalDestination ? [{
1512
- path: canonicalDestination.policyPath,
1513
- access: "write"
1514
- }] : []]);
1515
- if (record.recursive === false) {
1516
- const parentPath = posix.dirname(filePath);
1517
- if ((await fsBridge.stat({ filePath: parentPath }))?.type !== "directory") throw new JsonRpcProtocolError(JSON_RPC_NOT_FOUND, "parent directory not found");
1518
- }
1519
- await fsBridge.mkdirp({
1636
+ else await fsBridge.writeFile({
1520
1637
  filePath,
1638
+ data: decodeFileData(record.dataBase64),
1639
+ mkdir: false,
1521
1640
  pinnedPath: canonicalDestination?.pinnedPath
1522
1641
  });
1523
1642
  }
1524
- async function getMetadata(execServer, params) {
1525
- const record = requireObject(params, "fs/getMetadata params");
1526
- const filePath = resolveExecServerPath(requireString(record.path, "path"), "metadata path");
1527
- const fsSandboxPolicy = resolveFsSandboxPolicy(execServer, record);
1528
- assertResolvedFsSandboxAccess(fsSandboxPolicy, [{
1529
- path: filePath,
1530
- access: "read"
1531
- }]);
1532
- const readPolicy = await authorizePhysicalReadPath(execServer.fsBridge, fsSandboxPolicy, filePath);
1533
- const stat = await execServer.fsBridge.stat({
1534
- filePath,
1535
- ...readPolicy
1536
- });
1537
- if (!stat) throw new JsonRpcProtocolError(JSON_RPC_NOT_FOUND, "file not found");
1538
- return {
1539
- isDirectory: stat.type === "directory",
1540
- isFile: stat.type === "file",
1541
- isSymlink: false,
1542
- size: stat.size,
1543
- createdAtMs: 0,
1544
- modifiedAtMs: stat.mtimeMs ?? 0
1545
- };
1643
+ function decodeFileData(value) {
1644
+ if (typeof value !== "string") throw new Error("dataBase64 must be a string.");
1645
+ return Buffer.from(value, "base64");
1546
1646
  }
1547
1647
  async function readDirectory(execServer, params) {
1548
1648
  const record = requireObject(params, "fs/readDirectory params");
@@ -1783,7 +1883,7 @@ async function spawnSandboxChild(params) {
1783
1883
  escalation = setTimeout(() => signalLocal("SIGKILL"), SANDBOX_CHILD_TERM_GRACE_MS);
1784
1884
  escalation.unref?.();
1785
1885
  }
1786
- const reaped = await Promise.race([closed.promise.then(() => true), delay(SANDBOX_CHILD_REAP_TIMEOUT_MS).then(() => false)]).finally(() => clearTimeout(escalation));
1886
+ const reaped = await raceWithTimeout(closed.promise.then(() => true), SANDBOX_CHILD_REAP_TIMEOUT_MS, () => false, { ref: false }).finally(() => clearTimeout(escalation));
1787
1887
  await Promise.all(localSignals);
1788
1888
  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.`);
1789
1889
  }
@@ -1793,7 +1893,8 @@ async function spawnSandboxChild(params) {
1793
1893
  })()
1794
1894
  };
1795
1895
  params.owners.add(owner);
1796
- settled.then(() => params.owners.delete(owner), () => params.owners.delete(owner));
1896
+ const releaseOwner = () => params.owners.delete(owner);
1897
+ settled.then(releaseOwner, releaseOwner);
1797
1898
  const interrupt = async () => {
1798
1899
  await ready.promise;
1799
1900
  const interruptRemote = params.interruptRemote;
@@ -1806,7 +1907,7 @@ async function spawnSandboxChild(params) {
1806
1907
  if (remainingMs <= 0) throw new Error("Sandbox process interrupt timed out waiting for remote process admission");
1807
1908
  assertCurrent();
1808
1909
  if (await interruptRemote(remainingMs)) return;
1809
- await Promise.race([delay(Math.min(SANDBOX_CHILD_INTERRUPT_POLL_MS, Math.max(0, deadline - performance.now()))), exited.promise]);
1910
+ await raceWithTimeout(exited.promise, Math.min(SANDBOX_CHILD_INTERRUPT_POLL_MS, Math.max(0, deadline - performance.now())), () => void 0, { ref: false });
1810
1911
  }
1811
1912
  })();
1812
1913
  interruptions.add(interruption);
@@ -1873,11 +1974,6 @@ async function spawnSandboxChild(params) {
1873
1974
  ready.resolve();
1874
1975
  }
1875
1976
  }
1876
- function delay(ms) {
1877
- return new Promise((resolve) => {
1878
- setTimeout(resolve, ms).unref?.();
1879
- });
1880
- }
1881
1977
  //#endregion
1882
1978
  //#region extensions/codex/src/app-server/sandbox-exec-server/http.ts
1883
1979
  const SANDBOX_HTTP_STREAM_LINE_MAX_CHARS = 262144;
@@ -1961,11 +2057,12 @@ async function runSandboxHttpRequest(execServer, notifications, requestId, param
1961
2057
  }
1962
2058
  return termination;
1963
2059
  };
2060
+ const cleanup = () => terminate().catch((error) => {
2061
+ embeddedAgentLog.warn("codex sandbox http/request cleanup failed", { error });
2062
+ });
1964
2063
  const abortOnSessionClose = () => {
1965
2064
  lifecycle.failed = true;
1966
- terminate().catch((error) => {
1967
- embeddedAgentLog.warn("codex sandbox http/request cleanup failed", { error });
1968
- });
2065
+ cleanup();
1969
2066
  };
1970
2067
  notifications.signal.addEventListener("abort", abortOnSessionClose, { once: true });
1971
2068
  child.once("close", () => {
@@ -1975,35 +2072,8 @@ async function runSandboxHttpRequest(execServer, notifications, requestId, param
1975
2072
  if (error.code === "EPIPE" || error.code === "ERR_STREAM_DESTROYED") return;
1976
2073
  embeddedAgentLog.warn("codex sandbox http/request stdin write failed", { error });
1977
2074
  });
1978
- readSandboxHttpResponse({
1979
- child,
1980
- lifecycle,
1981
- terminate,
1982
- requestId,
1983
- notifications,
1984
- streamResponse: params.streamResponse
1985
- }).then(response.resolve, response.reject);
1986
- try {
1987
- if (notifications.signal.aborted) abortOnSessionClose();
1988
- else {
1989
- owner.assertCurrent();
1990
- child.stdin.end(JSON.stringify(params));
1991
- }
1992
- await completion.promise;
1993
- await termination;
1994
- } catch (error) {
1995
- lifecycle.failed = true;
1996
- response.reject(error);
1997
- await terminate().catch((cleanupError) => {
1998
- embeddedAgentLog.warn("codex sandbox http/request cleanup failed", { error: cleanupError });
1999
- });
2000
- throw error;
2001
- } finally {
2002
- notifications.signal.removeEventListener("abort", abortOnSessionClose);
2003
- }
2004
- }
2005
- function readSandboxHttpResponse(params) {
2006
- return new Promise((resolve, reject) => {
2075
+ const { streamResponse } = params;
2076
+ new Promise((resolve, reject) => {
2007
2077
  let headerResolved = false;
2008
2078
  let failed = false;
2009
2079
  let childFailure = null;
@@ -2023,13 +2093,11 @@ function readSandboxHttpResponse(params) {
2023
2093
  const fail = (message) => {
2024
2094
  if (failed) return;
2025
2095
  failed = true;
2026
- params.lifecycle.failed = true;
2027
- params.terminate().catch((error) => {
2028
- embeddedAgentLog.warn("codex sandbox http/request cleanup failed", { error });
2029
- });
2096
+ lifecycle.failed = true;
2097
+ cleanup();
2030
2098
  if (headerResolved) {
2031
- params.notifications.send("http/request/bodyDelta", {
2032
- requestId: params.requestId,
2099
+ notifications.send("http/request/bodyDelta", {
2100
+ requestId,
2033
2101
  seq: lastBodySeq + 1,
2034
2102
  deltaBase64: "",
2035
2103
  done: true,
@@ -2049,64 +2117,58 @@ function readSandboxHttpResponse(params) {
2049
2117
  }
2050
2118
  output.chunks.push(buffer);
2051
2119
  };
2052
- if (params.streamResponse) {
2053
- params.child.stdout.setEncoding("utf8");
2054
- params.child.stderr.setEncoding("utf8");
2055
- }
2056
- params.child.stdout.on("data", (chunk) => {
2057
- if (failed) return;
2058
- if (!params.streamResponse) {
2059
- bufferOutput("stdout", chunk);
2060
- return;
2061
- }
2062
- const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
2063
- stdoutBuffer += text;
2064
- let newline = stdoutBuffer.indexOf("\n");
2065
- while (newline >= 0) {
2066
- const line = stdoutBuffer.slice(0, newline).trim();
2067
- stdoutBuffer = stdoutBuffer.slice(newline + 1);
2068
- if (line) try {
2069
- const message = requireObject(JSON.parse(line), "http stream message");
2070
- const type = requireString(message.type, "http stream message type");
2071
- if (type === "headers") {
2072
- headerResolved = true;
2073
- resolve({
2074
- status: requireNumber(message.status, "http status"),
2075
- headers: readHttpHeaders(message.headers),
2076
- bodyBase64: ""
2077
- });
2078
- } else if (type === "bodyDelta") {
2079
- const seq = requireNumber(message.seq, "http body sequence");
2080
- lastBodySeq = Math.max(lastBodySeq, seq);
2081
- params.notifications.send("http/request/bodyDelta", {
2082
- requestId: params.requestId,
2083
- seq,
2084
- deltaBase64: typeof message.deltaBase64 === "string" ? message.deltaBase64 : "",
2085
- done: message.done === true,
2086
- error: typeof message.error === "string" ? message.error : null
2087
- });
2120
+ for (const stream of ["stdout", "stderr"]) {
2121
+ if (streamResponse) child[stream].setEncoding("utf8");
2122
+ child[stream].on("data", (chunk) => {
2123
+ if (failed) return;
2124
+ if (!streamResponse) {
2125
+ bufferOutput(stream, chunk);
2126
+ return;
2127
+ }
2128
+ const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
2129
+ if (stream === "stderr") {
2130
+ stderr = sliceUtf16Safe(`${stderr}${text}`, -4096);
2131
+ return;
2132
+ }
2133
+ stdoutBuffer += text;
2134
+ let newline = stdoutBuffer.indexOf("\n");
2135
+ while (newline >= 0) {
2136
+ const line = stdoutBuffer.slice(0, newline).trim();
2137
+ stdoutBuffer = stdoutBuffer.slice(newline + 1);
2138
+ if (line) try {
2139
+ const message = requireObject(JSON.parse(line), "http stream message");
2140
+ const type = requireString(message.type, "http stream message type");
2141
+ if (type === "headers") {
2142
+ headerResolved = true;
2143
+ resolve({
2144
+ status: requireNumber(message.status, "http status"),
2145
+ headers: readHttpHeaders(message.headers),
2146
+ bodyBase64: ""
2147
+ });
2148
+ } else if (type === "bodyDelta") {
2149
+ const seq = requireNumber(message.seq, "http body sequence");
2150
+ lastBodySeq = Math.max(lastBodySeq, seq);
2151
+ notifications.send("http/request/bodyDelta", {
2152
+ requestId,
2153
+ seq,
2154
+ deltaBase64: typeof message.deltaBase64 === "string" ? message.deltaBase64 : "",
2155
+ done: message.done === true,
2156
+ error: typeof message.error === "string" ? message.error : null
2157
+ });
2158
+ }
2159
+ } catch (error) {
2160
+ fail(error instanceof Error ? error.message : String(error));
2088
2161
  }
2089
- } catch (error) {
2090
- fail(error instanceof Error ? error.message : String(error));
2162
+ newline = stdoutBuffer.indexOf("\n");
2091
2163
  }
2092
- newline = stdoutBuffer.indexOf("\n");
2093
- }
2094
- 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`);
2095
- });
2096
- params.child.stderr.on("data", (chunk) => {
2097
- if (failed) return;
2098
- if (!params.streamResponse) {
2099
- bufferOutput("stderr", chunk);
2100
- return;
2101
- }
2102
- const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
2103
- stderr = sliceUtf16Safe(`${stderr}${text}`, -4096);
2104
- });
2105
- params.child.once("error", (error) => {
2164
+ 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`);
2165
+ });
2166
+ }
2167
+ child.once("error", (error) => {
2106
2168
  childFailure ??= error.message;
2107
- params.lifecycle.failed = true;
2169
+ lifecycle.failed = true;
2108
2170
  });
2109
- params.child.once("close", (code) => {
2171
+ child.once("close", (code) => {
2110
2172
  const exitCode = code ?? 1;
2111
2173
  if (failed) return;
2112
2174
  if (childFailure) {
@@ -2114,7 +2176,7 @@ function readSandboxHttpResponse(params) {
2114
2176
  return;
2115
2177
  }
2116
2178
  if (exitCode === 0) {
2117
- if (!params.streamResponse) {
2179
+ if (!streamResponse) {
2118
2180
  try {
2119
2181
  const parsed = JSON.parse(Buffer.concat(buffered.stdout.chunks).toString("utf8"));
2120
2182
  if (typeof parsed.status !== "number" || !Array.isArray(parsed.headers)) throw new Error("sandbox http/request returned an invalid response envelope");
@@ -2129,15 +2191,31 @@ function readSandboxHttpResponse(params) {
2129
2191
  return;
2130
2192
  }
2131
2193
  if (!headerResolved) {
2132
- params.lifecycle.failed = true;
2194
+ lifecycle.failed = true;
2133
2195
  reject(/* @__PURE__ */ new Error("sandbox http/request exited before returning headers"));
2134
2196
  }
2135
2197
  return;
2136
2198
  }
2137
- if (!params.streamResponse) stderr = Buffer.concat(buffered.stderr.chunks).toString("utf8");
2199
+ if (!streamResponse) stderr = Buffer.concat(buffered.stderr.chunks).toString("utf8");
2138
2200
  fail(stderr.trim() || `sandbox http/request failed with code ${exitCode}`);
2139
2201
  });
2140
- });
2202
+ }).then(response.resolve, response.reject);
2203
+ try {
2204
+ if (notifications.signal.aborted) abortOnSessionClose();
2205
+ else {
2206
+ owner.assertCurrent();
2207
+ child.stdin.end(JSON.stringify(params));
2208
+ }
2209
+ await completion.promise;
2210
+ await termination;
2211
+ } catch (error) {
2212
+ lifecycle.failed = true;
2213
+ response.reject(error);
2214
+ await cleanup();
2215
+ throw error;
2216
+ } finally {
2217
+ notifications.signal.removeEventListener("abort", abortOnSessionClose);
2218
+ }
2141
2219
  }
2142
2220
  const SANDBOX_HTTP_REQUEST_SCRIPT = String.raw`
2143
2221
  tmp=$(mktemp "$TMPDIR/openclaw-http.XXXXXX.py" 2>/dev/null || mktemp "/tmp/openclaw-http.XXXXXX.py") || exit 1
@@ -2155,9 +2233,6 @@ import urllib.request
2155
2233
  def emit(payload):
2156
2234
  print(json.dumps(payload, separators=(",", ":")), flush=True)
2157
2235
 
2158
- def response_headers(response):
2159
- return [{"name": name, "value": value} for name, value in response.headers.items()]
2160
-
2161
2236
  BLOCKED_HOSTNAMES = {
2162
2237
  "localhost",
2163
2238
  "localhost.localdomain",
@@ -2193,9 +2268,7 @@ def is_blocked_hostname(hostname):
2193
2268
  normalized = normalize_hostname(hostname)
2194
2269
  return (
2195
2270
  normalized in BLOCKED_HOSTNAMES
2196
- or normalized.endswith(".localhost")
2197
- or normalized.endswith(".local")
2198
- or normalized.endswith(".internal")
2271
+ or normalized.endswith((".localhost", ".local", ".internal"))
2199
2272
  )
2200
2273
 
2201
2274
  def is_blocked_ip(address):
@@ -2208,12 +2281,9 @@ def is_blocked_ip(address):
2208
2281
  return True
2209
2282
  if str(parsed).lower() in CLOUD_METADATA_IP_ADDRESSES:
2210
2283
  return True
2211
- if isinstance(parsed, ipaddress.IPv4Address):
2212
- if any(parsed in network for network in BLOCKED_IPV4_NETWORKS):
2213
- return True
2214
- else:
2215
- if any(parsed in network for network in BLOCKED_IPV6_NETWORKS):
2216
- return True
2284
+ networks = BLOCKED_IPV4_NETWORKS if isinstance(parsed, ipaddress.IPv4Address) else BLOCKED_IPV6_NETWORKS
2285
+ if any(parsed in network for network in networks):
2286
+ return True
2217
2287
  return (
2218
2288
  parsed.is_loopback
2219
2289
  or parsed.is_private
@@ -2314,7 +2384,7 @@ def pinned_getaddrinfo(original_getaddrinfo):
2314
2384
  return getaddrinfo
2315
2385
 
2316
2386
  def handle_response(input_data, response):
2317
- headers = response_headers(response)
2387
+ headers = [{"name": name, "value": value} for name, value in response.headers.items()]
2318
2388
  status = int(getattr(response, "status", getattr(response, "code", 0)))
2319
2389
  if input_data.get("streamResponse"):
2320
2390
  emit({"type": "headers", "status": status, "headers": headers})
@@ -2384,7 +2454,7 @@ async function startProcess(execServer, processes, notify, params, processAuthor
2384
2454
  if (processes.has(processId)) throw new Error(`process already exists: ${processId}`);
2385
2455
  const argv = requireStringArray(record.argv, "argv");
2386
2456
  const cwd = resolveExecServerPath(requireString(record.cwd, "cwd"), "process cwd");
2387
- rejectUnsupportedArg0(record.arg0);
2457
+ if (record.arg0 !== void 0 && record.arg0 !== null) throw new Error(typeof record.arg0 === "string" ? "Codex sandbox exec-server does not support arg0 overrides." : "arg0 must be a string or null.");
2388
2458
  assertSupportedProcessSandbox(execServer, record);
2389
2459
  const env = readProcessEnv(record);
2390
2460
  const managed = {
@@ -2449,7 +2519,7 @@ async function startProcess(execServer, processes, notify, params, processAuthor
2449
2519
  notifyProcessWaiters(managed);
2450
2520
  throw error;
2451
2521
  } finally {
2452
- if (managed.startPromise === startPromise) managed.startPromise = void 0;
2522
+ managed.startPromise = void 0;
2453
2523
  }
2454
2524
  return {
2455
2525
  processId,
@@ -2461,8 +2531,7 @@ function assertSupportedProcessSandbox(execServer, record) {
2461
2531
  if (record.enforceManagedNetwork === true || record.managedNetwork !== void 0 && record.managedNetwork !== null) throw new Error("Codex managed network restrictions cannot be enforced by the sandbox backend.");
2462
2532
  if (resolveFsSandboxPolicy(execServer, record)?.unrestricted === false) throw new Error("Codex process filesystem sandbox restrictions cannot be enforced by the backend.");
2463
2533
  if (record.sandbox === void 0 || record.sandbox === null) return;
2464
- if (requireObject(requireObject(record.sandbox, "process sandbox context").permissions, "process sandbox permissions").network !== "restricted") return;
2465
- if (!execServer.networkIsolated) throw new Error("Codex network restrictions cannot be enforced by the sandbox backend.");
2534
+ if (requireObject(requireObject(record.sandbox, "process sandbox context").permissions, "process sandbox permissions").network === "restricted" && !execServer.networkIsolated) throw new Error("Codex network restrictions cannot be enforced by the sandbox backend.");
2466
2535
  }
2467
2536
  async function runProcess(execServer, managed, params) {
2468
2537
  const backend = execServer.backend;
@@ -2522,14 +2591,12 @@ async function runProcess(execServer, managed, params) {
2522
2591
  const child = owner.process;
2523
2592
  child.stdout.on("data", (chunk) => appendProcessChunk(managed, "stdout", chunk));
2524
2593
  child.stderr.on("data", (chunk) => appendProcessChunk(managed, "stderr", chunk));
2525
- child.once("error", (error) => {
2594
+ const onError = (error) => {
2526
2595
  managed.failure ??= error.message;
2527
2596
  notifyProcessWaiters(managed);
2528
- });
2529
- child.stdin.on("error", (error) => {
2530
- managed.failure ??= error.message;
2531
- notifyProcessWaiters(managed);
2532
- });
2597
+ };
2598
+ child.once("error", onError);
2599
+ child.stdin.on("error", onError);
2533
2600
  owner.assertCurrent();
2534
2601
  if (!managed.tty && !managed.pipeStdin) child.stdin.end();
2535
2602
  }
@@ -2588,26 +2655,25 @@ function emitProcessClosed(managed, exitCode) {
2588
2655
  managed.evictProcess();
2589
2656
  notifyProcessWaiters(managed);
2590
2657
  }
2591
- function limitProcessChunks(chunks, maxBytes) {
2592
- if (!maxBytes) return chunks;
2593
- const retained = [];
2594
- let retainedBytes = 0;
2595
- for (const chunk of chunks) {
2596
- const byteLength = Buffer.from(chunk.chunk, "base64").byteLength;
2597
- if (retained.length > 0 && retainedBytes + byteLength > maxBytes) break;
2598
- retained.push(chunk);
2599
- retainedBytes += byteLength;
2600
- if (retainedBytes >= maxBytes) break;
2601
- }
2602
- return retained;
2603
- }
2604
2658
  async function readProcess(processes, params) {
2605
2659
  const record = requireObject(params, "process/read params");
2606
- const managed = requireProcess(processes, requireString(record.processId, "processId"));
2660
+ const processId = requireString(record.processId, "processId");
2661
+ const managed = processes.get(processId);
2662
+ if (!managed) throw new Error(`unknown process: ${processId}`);
2607
2663
  const afterSeq = typeof record.afterSeq === "number" ? record.afterSeq : 0;
2608
2664
  const waitMs = typeof record.waitMs === "number" && record.waitMs > 0 ? record.waitMs : 0;
2609
2665
  if (!managed.closed && managed.nextSeq - 1 <= afterSeq && waitMs > 0) await waitForProcessUpdate(managed, waitMs);
2610
- const chunks = limitProcessChunks(managed.chunks.filter((chunk) => chunk.seq > afterSeq), typeof record.maxBytes === "number" && record.maxBytes > 0 ? record.maxBytes : void 0);
2666
+ const maxBytes = typeof record.maxBytes === "number" && record.maxBytes > 0 ? record.maxBytes : void 0;
2667
+ const chunks = [];
2668
+ let retainedBytes = 0;
2669
+ for (const chunk of managed.chunks) {
2670
+ if (!(chunk.seq > afterSeq)) continue;
2671
+ const byteLength = maxBytes ? Buffer.from(chunk.chunk, "base64").byteLength : 0;
2672
+ if (maxBytes && chunks.length > 0 && retainedBytes + byteLength > maxBytes) break;
2673
+ chunks.push(chunk);
2674
+ retainedBytes += byteLength;
2675
+ if (maxBytes && retainedBytes >= maxBytes) break;
2676
+ }
2611
2677
  const lastChunk = chunks.at(-1);
2612
2678
  return {
2613
2679
  chunks,
@@ -2651,8 +2717,7 @@ async function signalProcess(processes, params) {
2651
2717
  async function terminateProcess(processes, params) {
2652
2718
  const processId = requireString(requireObject(params, "process/terminate params").processId, "processId");
2653
2719
  const managed = processes.get(processId);
2654
- if (!managed) return { running: false };
2655
- return await terminateManagedProcess(managed);
2720
+ return managed ? await terminateManagedProcess(managed) : { running: false };
2656
2721
  }
2657
2722
  async function terminateManagedProcess(managed) {
2658
2723
  const running = !managed.exited;
@@ -2678,16 +2743,6 @@ function notifyProcessWaiters(managed) {
2678
2743
  managed.waiters = [];
2679
2744
  for (const waiter of waiters) waiter();
2680
2745
  }
2681
- function requireProcess(processes, processId) {
2682
- const managed = processes.get(processId);
2683
- if (!managed) throw new Error(`unknown process: ${processId}`);
2684
- return managed;
2685
- }
2686
- function rejectUnsupportedArg0(value) {
2687
- if (value === void 0 || value === null) return;
2688
- if (typeof value === "string") throw new Error("Codex sandbox exec-server does not support arg0 overrides.");
2689
- throw new Error("arg0 must be a string or null.");
2690
- }
2691
2746
  function readEnv(value) {
2692
2747
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
2693
2748
  const env = {};
@@ -2695,22 +2750,18 @@ function readEnv(value) {
2695
2750
  return env;
2696
2751
  }
2697
2752
  function readProcessEnv(record) {
2698
- const requestedEnv = {
2699
- ...buildEnvFromPolicy(record.envPolicy),
2700
- ...readEnv(record.env)
2701
- };
2702
- return sanitizeEnvVars(requestedEnv).allowed;
2703
- }
2704
- function buildEnvFromPolicy(value) {
2705
- if (!value || typeof value !== "object" || Array.isArray(value)) return {};
2706
- const policy = value;
2707
- const inheritedEnv = readEnv(policy.set);
2708
- const includeOnly = filterStringEntries(policy.includeOnly);
2753
+ const value = record.envPolicy;
2754
+ const policy = value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
2755
+ const inheritedEnv = readEnv(policy?.set);
2756
+ const includeOnly = filterStringEntries(policy?.includeOnly);
2709
2757
  if (includeOnly.length > 0) {
2710
2758
  const regexes = includeOnly.map(wildcardPatternToRegex);
2711
2759
  for (const key of Object.keys(inheritedEnv)) if (!regexes.some((regex) => regex.test(key))) delete inheritedEnv[key];
2712
2760
  }
2713
- return inheritedEnv;
2761
+ return sanitizeEnvVars({
2762
+ ...inheritedEnv,
2763
+ ...readEnv(record.env)
2764
+ }).allowed;
2714
2765
  }
2715
2766
  function wildcardPatternToRegex(pattern) {
2716
2767
  const escaped = pattern.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
@@ -2788,14 +2839,12 @@ var CodexSandboxExecSession = class {
2788
2839
  case "fs/open": return await openFile(this.execServer, this.fileReads, params);
2789
2840
  case "fs/readBlock": return readFileBlock(this.fileReads, params);
2790
2841
  case "fs/close": return closeFile(this.fileReads, params);
2791
- case "fs/readFile": return await readFile(this.execServer, params);
2842
+ case "fs/readFile":
2843
+ case "fs/getMetadata": return await readFileOrMetadata(this.execServer, params, method);
2792
2844
  case "fs/writeFile":
2793
- await writeFile(this.execServer, params);
2794
- return {};
2795
2845
  case "fs/createDirectory":
2796
- await createDirectory(this.execServer, params);
2846
+ await writeFileOrDirectory(this.execServer, params, method);
2797
2847
  return {};
2798
- case "fs/getMetadata": return await getMetadata(this.execServer, params);
2799
2848
  case "fs/readDirectory": return await readDirectory(this.execServer, params);
2800
2849
  case "fs/remove":
2801
2850
  await removePath(this.execServer, params);
@@ -2825,10 +2874,8 @@ async function ensureCodexSandboxExecServerEnvironment(params) {
2825
2874
  if (placementNodeId && !params.runtime) throw new Error("Codex node execution requires its active plugin runtime.");
2826
2875
  if (!canExposeLocalExecServerToAppServer(params.appServerStartOptions)) throw new Error("OpenClaw Codex exec-server uses a local loopback URL and cannot be registered with a remote Codex app-server.");
2827
2876
  const { server: execServer, nodeLease } = await acquireOpenClawExecServer({
2828
- sandbox: params.sandbox,
2829
- runtime: params.runtime,
2830
- signal: params.signal,
2831
- onExecutionDisconnect: params.onExecutionDisconnect
2877
+ ...params,
2878
+ sandbox: params.sandbox
2832
2879
  });
2833
2880
  const processAuthority = params.requireProcessAuthority && !("node" in execServer) ? getCodexNativeProcessClient(params.client) : void 0;
2834
2881
  if (processAuthority && !("node" in execServer)) {
@@ -2885,7 +2932,16 @@ async function acquireOpenClawExecServer(params) {
2885
2932
  const { sandbox, runtime, signal, onExecutionDisconnect } = params;
2886
2933
  const key = sandbox.runtimeId;
2887
2934
  while (true) {
2888
- const promise = sandboxExecServerRegistry.servers.get(key) ?? startAndRememberOpenClawExecServer(sandbox);
2935
+ let promise = sandboxExecServerRegistry.servers.get(key);
2936
+ if (!promise) {
2937
+ const created = startOpenClawExecServer(sandbox);
2938
+ const createdKey = sandbox.runtimeId;
2939
+ sandboxExecServerRegistry.servers.set(createdKey, created);
2940
+ created.catch(() => {
2941
+ if (sandboxExecServerRegistry.servers.get(createdKey) === created) sandboxExecServerRegistry.servers.delete(createdKey);
2942
+ });
2943
+ promise = created;
2944
+ }
2889
2945
  const server = await promise;
2890
2946
  if (!server.closed && sandboxExecServerRegistry.servers.get(key) === promise) {
2891
2947
  server.refCount += 1;
@@ -2938,15 +2994,6 @@ async function acquireOpenClawExecServer(params) {
2938
2994
  }
2939
2995
  }
2940
2996
  }
2941
- function startAndRememberOpenClawExecServer(sandbox) {
2942
- const created = startOpenClawExecServer(sandbox);
2943
- const key = sandbox.runtimeId;
2944
- sandboxExecServerRegistry.servers.set(key, created);
2945
- created.catch(() => {
2946
- if (sandboxExecServerRegistry.servers.get(key) === created) sandboxExecServerRegistry.servers.delete(key);
2947
- });
2948
- return created;
2949
- }
2950
2997
  async function startOpenClawExecServer(sandbox) {
2951
2998
  const backend = sandbox.backend;
2952
2999
  const fsBridge = sandbox.fsBridge;
@@ -2973,7 +3020,7 @@ async function startOpenClawExecServer(sandbox) {
2973
3020
  await once(server, "listening");
2974
3021
  const address = server.address();
2975
3022
  if (!address || typeof address === "string") throw new Error("OpenClaw Codex exec-server did not bind to a TCP port.");
2976
- const environmentId = buildEnvironmentId(sandbox);
3023
+ const environmentId = `openclaw-sandbox-${createHash("sha256").update(sandbox.runtimeId).digest("hex").slice(0, 16)}`;
2977
3024
  const authPath = `/openclaw-${randomUUID()}`;
2978
3025
  const common = {
2979
3026
  authPath,
@@ -2999,7 +3046,9 @@ async function startOpenClawExecServer(sandbox) {
2999
3046
  networkIsolated: (connection.backend.id === "docker" || connection.backend.id === "podman") && sandbox.docker.network.trim().toLowerCase() === "none"
3000
3047
  };
3001
3048
  server.on("connection", (socket, request) => {
3002
- socket.on("error", handleExecServerSocketError);
3049
+ socket.on("error", (error) => {
3050
+ embeddedAgentLog.debug("codex sandbox exec-server websocket failed", { error });
3051
+ });
3003
3052
  const requestUrl = new URL(request.url ?? "", "ws://127.0.0.1");
3004
3053
  if (requestUrl.pathname !== execServer.authPath && ("node" in execServer || !execServer.processAuthorities?.has(requestUrl.pathname))) {
3005
3054
  socket.close(1008, "unauthorized");
@@ -3027,15 +3076,12 @@ async function releaseOpenClawExecServer(execServer) {
3027
3076
  if (current === execServer) sandboxExecServerRegistry.servers.delete(execServer.sandbox.runtimeId);
3028
3077
  await sandboxExecServerRegistry.close(execServer);
3029
3078
  }
3030
- function buildEnvironmentId(sandbox) {
3031
- return `openclaw-sandbox-${createHash("sha256").update(sandbox.runtimeId).digest("hex").slice(0, 16)}`;
3032
- }
3033
3079
  function readCodexPlacementNodeId(sandbox) {
3034
3080
  if (!("placementExecutionMode" in sandbox) || sandbox.placementExecutionMode !== "remote-exec" || !("placementNodeId" in sandbox) || typeof sandbox.placementNodeId !== "string" || !sandbox.placementNodeId) return;
3035
3081
  return sandbox.placementNodeId;
3036
3082
  }
3037
3083
  function readCodexPlacementWorkspaceIdentity(sandbox) {
3038
- if (!("placementEnvironmentId" in sandbox) || typeof sandbox.placementEnvironmentId !== "string" || !sandbox.placementEnvironmentId || sandbox.placementEnvironmentId.trim() !== sandbox.placementEnvironmentId || !("placementSessionId" in sandbox) || typeof sandbox.placementSessionId !== "string" || !sandbox.placementSessionId || sandbox.placementSessionId.trim() !== sandbox.placementSessionId || !("placementOwnerEpoch" in sandbox) || typeof sandbox.placementOwnerEpoch !== "number" || !Number.isSafeInteger(sandbox.placementOwnerEpoch) || sandbox.placementOwnerEpoch < 1 || !sandbox.sessionKey || sandbox.sessionKey.trim() !== sandbox.sessionKey) throw new Error("Codex node execution requires its exact placement workspace identity.");
3084
+ if (!("placementEnvironmentId" in sandbox) || !isWorkspaceIdentityString(sandbox.placementEnvironmentId) || !("placementSessionId" in sandbox) || !isWorkspaceIdentityString(sandbox.placementSessionId) || !("placementOwnerEpoch" in sandbox) || typeof sandbox.placementOwnerEpoch !== "number" || !Number.isSafeInteger(sandbox.placementOwnerEpoch) || sandbox.placementOwnerEpoch < 1 || !sandbox.sessionKey || sandbox.sessionKey.trim() !== sandbox.sessionKey) throw new Error("Codex node execution requires its exact placement workspace identity.");
3039
3085
  return {
3040
3086
  environmentId: sandbox.placementEnvironmentId,
3041
3087
  sessionId: sandbox.placementSessionId,
@@ -3043,6 +3089,9 @@ function readCodexPlacementWorkspaceIdentity(sandbox) {
3043
3089
  sessionKey: sandbox.sessionKey
3044
3090
  };
3045
3091
  }
3092
+ function isWorkspaceIdentityString(value) {
3093
+ return typeof value === "string" && value.length > 0 && value.trim() === value;
3094
+ }
3046
3095
  function handleNodeConnection(execServer, socket, requestUrl) {
3047
3096
  const leaseId = requestUrl.searchParams.get("lease");
3048
3097
  const lease = leaseId ? execServer.node.leases.get(leaseId) : void 0;
@@ -3097,9 +3146,6 @@ function trackExecServerCleanup(execServer, cleanup, failureMessage) {
3097
3146
  embeddedAgentLog.warn(failureMessage, { error: error instanceof Error ? error.message : String(error) });
3098
3147
  });
3099
3148
  }
3100
- function handleExecServerSocketError(error) {
3101
- embeddedAgentLog.debug("codex sandbox exec-server websocket failed", { error });
3102
- }
3103
3149
  async function handleMessage(session, data) {
3104
3150
  await session.handleRequest(parseRequest(codexWebSocketDataToBuffer(data).toString("utf8")));
3105
3151
  }
@@ -3197,10 +3243,19 @@ async function handleCodexAppServerApprovalRequest(params) {
3197
3243
  }
3198
3244
  });
3199
3245
  };
3246
+ const finishApproval = (outcome, { message, ...details }) => {
3247
+ emitEvent({
3248
+ phase: "resolved",
3249
+ ...details,
3250
+ ...params.method === "item/permissions/requestApproval" ? { scope: outcome === "approved-session" ? "session" : "turn" } : {},
3251
+ message
3252
+ });
3253
+ return buildApprovalResponse(params.method, requestParams, outcome);
3254
+ };
3200
3255
  if (params.signal?.aborted) {
3201
3256
  if (params.signal.reason instanceof CodexServerRequestResolvedError) return;
3202
3257
  recordNativeToolFailureDisposition(params, context, "cancelled");
3203
- return buildApprovalResponse(params.method, context.requestParams, "cancelled");
3258
+ return buildApprovalResponse(params.method, requestParams, "cancelled");
3204
3259
  }
3205
3260
  let revalidateMutableFileApproval;
3206
3261
  let mutableFileApprovalRequiresOneShot = false;
@@ -3220,7 +3275,7 @@ async function handleCodexAppServerApprovalRequest(params) {
3220
3275
  resolvedMessage = "Codex app-server approval granted for this byte-bound command only.";
3221
3276
  }
3222
3277
  if (params.method === "item/commandExecution/requestApproval" && resolvedOutcome !== "denied") {
3223
- const resolution = resolveCommandApproval(context.requestParams, resolvedOutcome);
3278
+ const resolution = resolveCommandApproval(requestParams, resolvedOutcome);
3224
3279
  if (resolution.scope === "denied") {
3225
3280
  resolvedOutcome = "denied";
3226
3281
  resolvedMessage = "Codex app-server request does not offer the approved scope.";
@@ -3231,17 +3286,14 @@ async function handleCodexAppServerApprovalRequest(params) {
3231
3286
  }
3232
3287
  params.signal?.throwIfAborted();
3233
3288
  if (resolvedOutcome !== "denied") params.paramsForRun.hostCapabilities.assertActive();
3234
- emitEvent({
3235
- phase: "resolved",
3289
+ return finishApproval(resolvedOutcome, {
3236
3290
  status: resolvedOutcome === "denied" ? "denied" : "approved",
3237
3291
  ...resolvedApprovalId ? {
3238
3292
  approvalId: resolvedApprovalId,
3239
3293
  approvalSlug: resolvedApprovalId
3240
3294
  } : {},
3241
- ...approvalEventScope(params.method, resolvedOutcome),
3242
3295
  message: resolvedMessage
3243
3296
  });
3244
- return buildApprovalResponse(params.method, context.requestParams, resolvedOutcome);
3245
3297
  };
3246
3298
  try {
3247
3299
  if (params.method === "item/commandExecution/requestApproval" && !readNetworkApprovalContext(requestParams)) {
@@ -3271,7 +3323,7 @@ async function handleCodexAppServerApprovalRequest(params) {
3271
3323
  recordNativeToolFailureDisposition(params, context, policyOutcome.failureDisposition);
3272
3324
  return await resolvePolicyApproval("denied", policyOutcome.reason);
3273
3325
  }
3274
- if (policyOutcome?.outcome === "approved-once" || policyOutcome?.outcome === "approved-session") return await resolvePolicyApproval(policyOutcome.outcome);
3326
+ if (policyOutcome?.outcome === "approved-once") return await resolvePolicyApproval(policyOutcome.outcome);
3275
3327
  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.");
3276
3328
  const allowedDecisions = nativeApprovalAllowedDecisions({
3277
3329
  method: params.method,
@@ -3293,13 +3345,10 @@ async function handleCodexAppServerApprovalRequest(params) {
3293
3345
  params.signal?.throwIfAborted();
3294
3346
  if (!approvalId) {
3295
3347
  recordNativeToolFailureDisposition(params, context, "failed");
3296
- emitEvent({
3297
- phase: "resolved",
3348
+ return finishApproval("denied", {
3298
3349
  status: "unavailable",
3299
- ...approvalEventScope(params.method, "denied"),
3300
3350
  message: "Codex app-server approval route unavailable."
3301
3351
  });
3302
- return buildApprovalResponse(params.method, context.requestParams, "denied");
3303
3352
  }
3304
3353
  emitEvent({
3305
3354
  phase: "requested",
@@ -3319,30 +3368,24 @@ async function handleCodexAppServerApprovalRequest(params) {
3319
3368
  if (approvalTimedOut) recordNativeToolFailureDisposition(params, context, "timed_out", context.approvalKind);
3320
3369
  else if (outcome === "unavailable") recordNativeToolFailureDisposition(params, context, "failed");
3321
3370
  if (outcome === "approved-once" || outcome === "approved-session") return await resolvePolicyApproval(outcome, approvalResolutionMessage(outcome), approvalId);
3322
- emitEvent({
3323
- phase: "resolved",
3371
+ return finishApproval(outcome, {
3324
3372
  status: outcome,
3325
3373
  approvalId,
3326
3374
  approvalSlug: approvalId,
3327
- ...approvalEventScope(params.method, outcome),
3328
3375
  message: approvalTimedOut ? codexApprovalTimeoutText(context.approvalKind) : approvalResolutionMessage(outcome)
3329
3376
  });
3330
- return buildApprovalResponse(params.method, context.requestParams, outcome);
3331
3377
  } catch (error) {
3332
3378
  if (params.signal?.reason instanceof CodexServerRequestResolvedError) return;
3333
3379
  const cancelled = params.signal?.aborted === true;
3334
3380
  recordNativeToolFailureDisposition(params, context, cancelled && params.signal ? resolveCodexToolAbortTerminalReason(params.signal) : "failed");
3335
- emitEvent({
3336
- phase: "resolved",
3381
+ return finishApproval(cancelled ? "cancelled" : "denied", {
3337
3382
  status: cancelled ? "failed" : "unavailable",
3338
3383
  ...approvalId ? {
3339
3384
  approvalId,
3340
3385
  approvalSlug: approvalId
3341
3386
  } : {},
3342
- ...approvalEventScope(params.method, cancelled ? "cancelled" : "denied"),
3343
3387
  message: cancelled ? "Codex app-server approval cancelled because the run stopped." : `Codex app-server approval route failed: ${formatCodexDisplayText(coerceErrorMessage(error))}`
3344
3388
  });
3345
- return buildApprovalResponse(params.method, context.requestParams, cancelled ? "cancelled" : "denied");
3346
3389
  }
3347
3390
  }
3348
3391
  function recordNativeToolFailureDisposition(params, context, disposition, approvalKind) {
@@ -3354,17 +3397,17 @@ function recordNativeToolFailureDisposition(params, context, disposition, approv
3354
3397
  }
3355
3398
  function buildApprovalResponse(method, requestParams, outcome) {
3356
3399
  if (method === "item/commandExecution/requestApproval") return { decision: resolveCommandApproval(requestParams, outcome).decision };
3357
- if (method === "item/fileChange/requestApproval") return { decision: fileChangeApprovalDecision(outcome) };
3358
- if (method === "item/permissions/requestApproval") {
3359
- if (outcome === "approved-session" || outcome === "approved-once") return {
3360
- permissions: requestedPermissions(requestParams),
3361
- scope: outcome === "approved-session" ? "session" : "turn"
3362
- };
3363
- return {
3364
- permissions: {},
3365
- scope: "turn"
3366
- };
3367
- }
3400
+ if (method === "item/fileChange/requestApproval") return { decision: {
3401
+ cancelled: "cancel",
3402
+ denied: "decline",
3403
+ unavailable: "decline",
3404
+ "approved-session": "acceptForSession",
3405
+ "approved-once": "accept"
3406
+ }[outcome] };
3407
+ if (method === "item/permissions/requestApproval") return {
3408
+ permissions: outcome === "approved-session" || outcome === "approved-once" ? requestedPermissions(requestParams) : {},
3409
+ scope: outcome === "approved-session" ? "session" : "turn"
3410
+ };
3368
3411
  return {
3369
3412
  decision: "decline",
3370
3413
  reason: "OpenClaw codex app-server bridge does not grant native approvals yet."
@@ -3374,7 +3417,7 @@ function buildApprovalContext(params) {
3374
3417
  const itemId = readStringField(params.requestParams, "itemId") ?? readStringField(params.requestParams, "callId") ?? readStringField(params.requestParams, "approvalId");
3375
3418
  const approvalId = readStringField(params.requestParams, "approvalId") ?? itemId;
3376
3419
  const commandDetailLines = params.method === "item/commandExecution/requestApproval" ? describeCommandApprovalDetails(params.requestParams) : [];
3377
- const commandPreview = sanitizeApprovalPreview(readCommandActionsPreview(params.requestParams) ?? readCommandPreview(params.requestParams), commandDetailLines.length > 0 ? COMMAND_PREVIEW_WITH_DETAILS_MAX_LENGTH : 180);
3420
+ const commandPreview = sanitizeApprovalPreview(readJoinedPreview(readCommandActions(params.requestParams), " && ") ?? readCommandPreview(params.requestParams), commandDetailLines.length > 0 ? COMMAND_PREVIEW_WITH_DETAILS_MAX_LENGTH : 180);
3378
3421
  const reasonPreview = sanitizeApprovalPreview(readStringPreview(params.requestParams, "reason"), 180);
3379
3422
  const command = commandPreview.text;
3380
3423
  const reason = reasonPreview.text;
@@ -3382,7 +3425,8 @@ function buildApprovalContext(params) {
3382
3425
  const approvalKind = params.method.includes("commandExecution") ? "command" : params.method.includes("fileChange") ? "file-change" : params.method.includes("permissions") ? "permissions" : "other";
3383
3426
  const kind = approvalKind === "command" ? "exec" : approvalKind === "other" ? "unknown" : "plugin";
3384
3427
  const permissionLines = params.method === "item/permissions/requestApproval" ? describePermissionProfile(requestedPermissions(params.requestParams), "Permissions") : [];
3385
- const title = networkApproval ? "Codex app-server network approval" : kind === "exec" ? "Codex app-server command approval" : params.method === "item/permissions/requestApproval" ? "Codex app-server permission approval" : kind === "plugin" ? "Codex app-server file approval" : "Codex app-server approval";
3428
+ const approvalType = networkApproval ? "network" : kind === "exec" ? "command" : params.method === "item/permissions/requestApproval" ? "permission" : kind === "plugin" ? "file" : void 0;
3429
+ const title = `Codex app-server ${approvalType ? `${approvalType} ` : ""}approval`;
3386
3430
  const subject = (networkApproval ? `Network: ${sanitizeCodexApprovalVisibleText(networkApproval.protocol)}://${sanitizePermissionHostValue(networkApproval.host)}` : void 0) ?? permissionLines[0] ?? (command ? `Command: ${formatApprovalPreviewSubject(command, commandPreview.omitted)}` : commandPreview.omitted ? `Command: ${APPROVAL_PREVIEW_OMITTED}` : reason ? `Reason: ${formatApprovalPreviewSubject(reason, reasonPreview.omitted)}` : reasonPreview.omitted ? `Reason: ${APPROVAL_PREVIEW_OMITTED}` : `Request method: ${params.method}`);
3387
3431
  return {
3388
3432
  approvalKind,
@@ -3390,10 +3434,9 @@ function buildApprovalContext(params) {
3390
3434
  title,
3391
3435
  description: permissionLines.length > 0 ? joinDescriptionLinesWithinLimit(permissionLines, PERMISSION_DESCRIPTION_MAX_LENGTH) : [subject, ...commandDetailLines].join("\n"),
3392
3436
  severity: kind === "exec" ? "warning" : "info",
3393
- toolName: networkApproval ? "codex_network_approval" : kind === "exec" ? "codex_command_approval" : params.method === "item/permissions/requestApproval" ? "codex_permission_approval" : "codex_file_approval",
3437
+ toolName: `codex_${approvalType ?? "file"}_approval`,
3394
3438
  itemId,
3395
3439
  approvalId,
3396
- requestParams: params.requestParams,
3397
3440
  eventDetails: {
3398
3441
  ...itemId ? { itemId } : {},
3399
3442
  ...command ? { command } : {},
@@ -3407,24 +3450,9 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
3407
3450
  const policyRequest = buildOpenClawToolPolicyRequest(params.method, params.requestParams);
3408
3451
  if (!policyRequest) return;
3409
3452
  const cwd = readStringField(params.requestParams, "cwd") ?? params.paramsForRun.workspaceDir;
3410
- const nativeRelayOutcome = await runNativeRelayToolPolicyForApprovalRequest({
3411
- method: params.method,
3412
- requestParams: params.requestParams,
3413
- context: params.context,
3414
- policyRequest,
3415
- nativeHookRelay: params.nativeHookRelay,
3416
- autoApprove: params.autoApprove,
3417
- assertActive: params.paramsForRun.hostCapabilities.assertActive,
3418
- cwd,
3419
- signal: params.signal
3420
- });
3421
- if (nativeRelayOutcome?.blocked) return {
3422
- outcome: "denied",
3423
- reason: nativeRelayOutcome.reason,
3424
- ...nativeRelayOutcome.failureDisposition ? { failureDisposition: nativeRelayOutcome.failureDisposition } : {}
3425
- };
3426
- if (nativeRelayOutcome?.outcome === "approved-once" || nativeRelayOutcome?.outcome === "approved-session") return { outcome: nativeRelayOutcome.outcome };
3427
- if (nativeRelayOutcome?.handled) return { outcome: "allowed" };
3453
+ const assertActive = params.paramsForRun.hostCapabilities.assertActive;
3454
+ const nativeRelayOutcome = await runNativeRelayPolicy(policyRequest.params);
3455
+ if (nativeRelayOutcome) return nativeRelayOutcome;
3428
3456
  const outcome = await params.paramsForRun.hostCapabilities.runBeforeToolCall({
3429
3457
  toolName: policyRequest.toolName,
3430
3458
  params: policyRequest.params,
@@ -3443,105 +3471,86 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
3443
3471
  };
3444
3472
  if (outcome.approvalResolution) return { outcome: "approved-once" };
3445
3473
  return { outcome: "allowed" };
3446
- }
3447
- async function runNativeRelayToolPolicyForApprovalRequest(params) {
3448
- const nativeHookRelay = params.nativeHookRelay;
3449
- if (params.method !== "item/commandExecution/requestApproval" || !nativeHookRelay?.allowedEvents.includes("pre_tool_use")) return;
3450
- const payload = buildNativeRelayPreToolUsePayload({
3451
- requestParams: params.requestParams,
3452
- policyRequest: params.policyRequest,
3453
- context: params.context,
3454
- cwd: params.cwd
3455
- });
3456
- if (!payload) return;
3457
- const resolveDeferredApproval = async () => {
3458
- const approvalOutcome = await resolveNativeHookRelayDeferredToolApproval({
3459
- relayId: nativeHookRelay.relayId,
3460
- toolUseId: params.context.approvalId,
3461
- signal: params.signal
3462
- });
3463
- params.assertActive();
3464
- if (approvalOutcome?.outcome === "denied") return {
3465
- handled: true,
3466
- blocked: true,
3467
- reason: approvalOutcome.reason,
3468
- ...approvalOutcome.failureDisposition ? { failureDisposition: approvalOutcome.failureDisposition } : {}
3474
+ async function runNativeRelayPolicy(toolParams) {
3475
+ const nativeHookRelay = params.nativeHookRelay;
3476
+ if (params.method !== "item/commandExecution/requestApproval" || !nativeHookRelay?.allowedEvents.includes("pre_tool_use")) return;
3477
+ const command = readStringField(toolParams, "command");
3478
+ if (!command) return;
3479
+ const turnId = readStringField(params.requestParams, "turnId");
3480
+ const payload = {
3481
+ hook_event_name: "PreToolUse",
3482
+ openclaw_approval_mode: "report",
3483
+ tool_name: "exec_command",
3484
+ ...params.context.approvalId ? { tool_use_id: params.context.approvalId } : {},
3485
+ ...cwd ? { cwd } : {},
3486
+ ...turnId ? { turn_id: turnId } : {},
3487
+ tool_input: {
3488
+ ...toolParams,
3489
+ command,
3490
+ cmd: command
3491
+ }
3469
3492
  };
3470
- return approvalOutcome?.outcome === "approved-once" ? {
3471
- handled: true,
3472
- outcome: approvalOutcome.outcome
3473
- } : { handled: true };
3474
- };
3475
- if (hasNativeHookRelayInvocation({
3476
- relayId: nativeHookRelay.relayId,
3477
- event: "pre_tool_use",
3478
- toolUseId: params.context.approvalId
3479
- })) return resolveDeferredApproval();
3480
- try {
3481
- const decision = readNativeRelayPreToolUseDecision(await invokeNativeHookRelay({
3482
- provider: "codex",
3483
- relayId: nativeHookRelay.relayId,
3484
- generation: nativeHookRelay.generation,
3485
- event: "pre_tool_use",
3486
- rawPayload: payload,
3487
- requireGeneration: true
3488
- }));
3489
- if (decision.blocked) return {
3490
- handled: true,
3491
- blocked: true,
3492
- reason: decision.reason,
3493
- ...decision.failureDisposition ? { failureDisposition: decision.failureDisposition } : {}
3493
+ const resolveDeferredApproval = async () => {
3494
+ const approvalOutcome = await resolveNativeHookRelayDeferredToolApproval({
3495
+ relayId: nativeHookRelay.relayId,
3496
+ toolUseId: params.context.approvalId,
3497
+ signal: params.signal
3498
+ });
3499
+ assertActive();
3500
+ if (approvalOutcome?.outcome === "denied") return {
3501
+ outcome: "denied",
3502
+ reason: approvalOutcome.reason,
3503
+ ...approvalOutcome.failureDisposition ? { failureDisposition: approvalOutcome.failureDisposition } : {}
3504
+ };
3505
+ return approvalOutcome?.outcome === "approved-once" ? { outcome: approvalOutcome.outcome } : { outcome: "allowed" };
3494
3506
  };
3495
- return await resolveDeferredApproval();
3496
- } catch (error) {
3497
- if (params.autoApprove === true && !hasNativeHookRelayInvocation({
3507
+ if (hasNativeHookRelayInvocation({
3498
3508
  relayId: nativeHookRelay.relayId,
3499
3509
  event: "pre_tool_use",
3500
3510
  toolUseId: params.context.approvalId
3501
- })) return;
3502
- return {
3503
- handled: true,
3504
- blocked: true,
3505
- reason: `OpenClaw native hook relay unavailable for Codex app-server approval: ${formatCodexDisplayText(coerceErrorMessage(error))}`,
3506
- failureDisposition: "failed"
3507
- };
3508
- }
3509
- }
3510
- function buildNativeRelayPreToolUsePayload(params) {
3511
- const command = readStringField(params.policyRequest.params, "command");
3512
- if (!command) return;
3513
- const turnId = readStringField(params.requestParams, "turnId");
3514
- return {
3515
- hook_event_name: "PreToolUse",
3516
- openclaw_approval_mode: "report",
3517
- tool_name: "exec_command",
3518
- ...params.context.approvalId ? { tool_use_id: params.context.approvalId } : {},
3519
- ...params.cwd ? { cwd: params.cwd } : {},
3520
- ...turnId ? { turn_id: turnId } : {},
3521
- tool_input: {
3522
- ...params.policyRequest.params,
3523
- command,
3524
- cmd: command
3511
+ })) return resolveDeferredApproval();
3512
+ try {
3513
+ const denial = readNativeRelayPreToolUseDenial(await invokeNativeHookRelay({
3514
+ provider: "codex",
3515
+ relayId: nativeHookRelay.relayId,
3516
+ generation: nativeHookRelay.generation,
3517
+ event: "pre_tool_use",
3518
+ rawPayload: payload,
3519
+ requireGeneration: true
3520
+ }));
3521
+ if (denial) return denial;
3522
+ return await resolveDeferredApproval();
3523
+ } catch (error) {
3524
+ if (params.autoApprove === true && !hasNativeHookRelayInvocation({
3525
+ relayId: nativeHookRelay.relayId,
3526
+ event: "pre_tool_use",
3527
+ toolUseId: params.context.approvalId
3528
+ })) return;
3529
+ return {
3530
+ outcome: "denied",
3531
+ reason: `OpenClaw native hook relay unavailable for Codex app-server approval: ${formatCodexDisplayText(coerceErrorMessage(error))}`,
3532
+ failureDisposition: "failed"
3533
+ };
3525
3534
  }
3526
- };
3535
+ }
3527
3536
  }
3528
- function readNativeRelayPreToolUseDecision(response) {
3537
+ function readNativeRelayPreToolUseDenial(response) {
3529
3538
  if (!response || response.exitCode !== 0) return {
3530
- blocked: true,
3539
+ outcome: "denied",
3531
3540
  reason: sanitizeRelayDecisionReason(response?.stderr) || sanitizeRelayDecisionReason(response?.stdout) || "OpenClaw native hook relay failed for Codex app-server approval.",
3532
3541
  failureDisposition: response?.failureDisposition ?? "failed"
3533
3542
  };
3534
3543
  const stdout = response.stdout?.trim();
3535
- if (!stdout) return { blocked: false };
3544
+ if (!stdout) return;
3536
3545
  const parsed = safeParseJson(stdout);
3537
3546
  const output = isJsonObject(parsed) && isJsonObject(parsed.hookSpecificOutput) ? parsed.hookSpecificOutput : void 0;
3538
3547
  if (output?.permissionDecision === "deny") return {
3539
- blocked: true,
3548
+ outcome: "denied",
3540
3549
  reason: readStringField(output, "permissionDecisionReason") || "OpenClaw native hook policy denied Codex app-server approval.",
3541
3550
  ...response.failureDisposition ? { failureDisposition: response.failureDisposition } : {}
3542
3551
  };
3543
3552
  return {
3544
- blocked: true,
3553
+ outcome: "denied",
3545
3554
  reason: output ? "OpenClaw native hook relay returned a non-deny Codex app-server approval decision." : "OpenClaw native hook relay returned an unreadable Codex app-server approval result.",
3546
3555
  failureDisposition: "failed"
3547
3556
  };
@@ -3605,34 +3614,22 @@ function nativeApprovalAllowedDecisions(params) {
3605
3614
  ];
3606
3615
  return params.method === "item/commandExecution/requestApproval" ? commandApprovalAllowedDecisions(params.requestParams, params.requiresOneShot) : void 0;
3607
3616
  }
3608
- function fileChangeApprovalDecision(outcome) {
3609
- if (outcome === "cancelled") return "cancel";
3610
- if (outcome === "denied" || outcome === "unavailable") return "decline";
3611
- return outcome === "approved-session" ? "acceptForSession" : "accept";
3612
- }
3613
3617
  function requestedPermissions(requestParams) {
3614
3618
  const permissions = isJsonObject(requestParams?.permissions) ? requestParams.permissions : {};
3615
- const granted = {};
3616
- if (isJsonObject(permissions.network)) granted.network = permissions.network;
3617
- if (isJsonObject(permissions.fileSystem)) granted.fileSystem = permissions.fileSystem;
3618
- return granted;
3619
+ return Object.fromEntries(["network", "fileSystem"].flatMap((key) => isJsonObject(permissions[key]) ? [[key, permissions[key]]] : []));
3619
3620
  }
3620
3621
  function describeCommandApprovalDetails(requestParams) {
3621
- const lines = [];
3622
3622
  const additionalPermissions = isJsonObject(requestParams?.additionalPermissions) ? requestParams.additionalPermissions : void 0;
3623
- if (additionalPermissions) lines.push(...describePermissionProfile(additionalPermissions, "Additional permissions"));
3624
- const execpolicySummary = summarizeStringArray(requestParams?.proposedExecpolicyAmendment, "Proposed exec policy", sanitizeCodexApprovalVisibleText);
3625
- if (execpolicySummary) lines.push(execpolicySummary);
3626
- const networkAmendmentSummary = summarizeNetworkPolicyAmendments(requestParams?.proposedNetworkPolicyAmendments);
3627
- if (networkAmendmentSummary) lines.push(networkAmendmentSummary);
3628
- return lines;
3623
+ return [
3624
+ ...additionalPermissions ? describePermissionProfile(additionalPermissions, "Additional permissions") : [],
3625
+ summarizeExecPolicyAmendment(requestParams?.proposedExecpolicyAmendment),
3626
+ summarizePermissionTargets(requestParams?.proposedNetworkPolicyAmendments, { kind: "network" })
3627
+ ].filter((line) => Boolean(line));
3629
3628
  }
3630
3629
  function describePermissionProfile(permissions, label) {
3631
3630
  const lines = [];
3632
- const kinds = [];
3633
3631
  const risks = /* @__PURE__ */ new Set();
3634
- if (isJsonObject(permissions.network)) kinds.push("network");
3635
- if (isJsonObject(permissions.fileSystem)) kinds.push("fileSystem");
3632
+ const kinds = ["network", "fileSystem"].filter((kind) => isJsonObject(permissions[kind]));
3636
3633
  if (kinds.length > 0) lines.push(`${label}: ${kinds.join(", ")}`);
3637
3634
  let networkSummary;
3638
3635
  if (isJsonObject(permissions.network)) {
@@ -3654,7 +3651,10 @@ function describePermissionProfile(permissions, label) {
3654
3651
  ].map((key) => summarizePermissionArray(fileSystem[key], key, risks, {
3655
3652
  sanitize: sanitizePermissionPathValue,
3656
3653
  risksFor: permissionPathRisks
3657
- })), summarizeFileSystemEntries(fileSystem, risks)].filter((summary) => Boolean(summary));
3654
+ })), summarizePermissionTargets(fileSystem.entries, {
3655
+ kind: "file-system",
3656
+ risks
3657
+ })].filter((summary) => Boolean(summary));
3658
3658
  fileSystemSummary = summaries.length > 0 ? summaries.join("; ") : void 0;
3659
3659
  }
3660
3660
  if (risks.size > 0) lines.push(`High-risk targets: ${[...risks].join(", ")}`);
@@ -3668,21 +3668,24 @@ function summarizeNetworkEnabledPermission(permission, risks) {
3668
3668
  if (enabled) risks.add("network access");
3669
3669
  return `enabled: ${enabled}`;
3670
3670
  }
3671
- function summarizeFileSystemEntries(permission, risks) {
3672
- const entries = permission.entries;
3671
+ function summarizePermissionTargets(entries, format) {
3673
3672
  if (!Array.isArray(entries)) return;
3673
+ const fileSystem = format.kind === "file-system";
3674
+ const sanitizeTarget = fileSystem ? sanitizePermissionPathValue : sanitizePermissionHostValue;
3674
3675
  const samples = [];
3675
3676
  let count = 0;
3676
3677
  for (const entry of entries) {
3677
3678
  const item = isJsonObject(entry) ? entry : void 0;
3678
- const path = typeof item?.path === "string" ? item.path.trim() : "";
3679
- const access = typeof item?.access === "string" ? item.access.trim() : "";
3680
- if (!path || !access) continue;
3679
+ const rawTarget = item?.[fileSystem ? "path" : "host"];
3680
+ const rawAction = item?.[fileSystem ? "access" : "action"];
3681
+ const target = typeof rawTarget === "string" ? fileSystem ? rawTarget.trim() : rawTarget : "";
3682
+ const action = typeof rawAction === "string" ? fileSystem ? rawAction.trim() : rawAction : "";
3683
+ if (!target || !action) continue;
3681
3684
  count += 1;
3682
- if (access !== "none") for (const risk of permissionPathRisks(path)) risks.add(risk);
3683
- if (samples.length < PERMISSION_SAMPLE_LIMIT) samples.push(`${sanitizeCodexApprovalVisibleText(access)} ${sanitizePermissionPathValue(path)}`);
3685
+ if (format.kind === "file-system" && action !== "none") for (const risk of permissionPathRisks(target)) format.risks.add(risk);
3686
+ if (samples.length < PERMISSION_SAMPLE_LIMIT) samples.push(`${sanitizeCodexApprovalVisibleText(action)} ${sanitizeTarget(target)}`);
3684
3687
  }
3685
- return formatPermissionSamples("entries", samples, count);
3688
+ return formatPermissionSamples(fileSystem ? "entries" : "Proposed network policy", samples, count);
3686
3689
  }
3687
3690
  function summarizePermissionArray(input, label, risks, format) {
3688
3691
  const values = normalizeTrimmedStringList(input);
@@ -3690,24 +3693,10 @@ function summarizePermissionArray(input, label, risks, format) {
3690
3693
  for (const value of values) for (const risk of format.risksFor(value)) risks.add(risk);
3691
3694
  return formatPermissionSamples(label, values.slice(0, PERMISSION_SAMPLE_LIMIT).map(format.sanitize).filter(Boolean), values.length);
3692
3695
  }
3693
- function summarizeStringArray(value, label, sanitize) {
3696
+ function summarizeExecPolicyAmendment(value) {
3694
3697
  if (!Array.isArray(value)) return;
3695
- const values = value.filter((entry) => typeof entry === "string").map((entry) => sanitize(entry)).filter(Boolean);
3696
- return formatPermissionSamples(label, values.slice(0, PERMISSION_SAMPLE_LIMIT), values.length);
3697
- }
3698
- function summarizeNetworkPolicyAmendments(value) {
3699
- if (!Array.isArray(value)) return;
3700
- const samples = [];
3701
- let count = 0;
3702
- for (const entry of value) {
3703
- const amendment = isJsonObject(entry) ? entry : void 0;
3704
- const host = typeof amendment?.host === "string" ? amendment.host : "";
3705
- const action = typeof amendment?.action === "string" ? amendment.action : "";
3706
- if (!host || !action) continue;
3707
- count += 1;
3708
- if (samples.length < PERMISSION_SAMPLE_LIMIT) samples.push(`${sanitizeCodexApprovalVisibleText(action)} ${sanitizePermissionHostValue(host)}`);
3709
- }
3710
- return formatPermissionSamples("Proposed network policy", samples, count);
3698
+ const values = value.filter((entry) => typeof entry === "string").map((entry) => sanitizeCodexApprovalVisibleText(entry)).filter(Boolean);
3699
+ return formatPermissionSamples("Proposed exec policy", values.slice(0, PERMISSION_SAMPLE_LIMIT), values.length);
3711
3700
  }
3712
3701
  function formatPermissionSamples(label, samples, count) {
3713
3702
  if (count === 0) return;
@@ -3727,18 +3716,12 @@ function sanitizePermissionPathValue(value) {
3727
3716
  }
3728
3717
  function permissionHostRisks(value) {
3729
3718
  const normalized = value.trim().toLowerCase();
3730
- const risks = [];
3731
- if (normalized.includes("*")) {
3732
- risks.push("wildcard hosts");
3733
- if (isPrivateNetworkHostPattern(normalized)) risks.push("private-network wildcards");
3734
- }
3735
- return risks;
3719
+ if (!normalized.includes("*")) return [];
3720
+ return isPrivateNetworkHostPattern(normalized) ? ["wildcard hosts", "private-network wildcards"] : ["wildcard hosts"];
3736
3721
  }
3737
3722
  function permissionPathRisks(value) {
3738
3723
  const normalized = sanitizeCodexApprovalVisibleText(value);
3739
- const risks = [];
3740
- if (normalized === "/" || normalized === "\\" || /^[A-Za-z]:[\\/]*$/.test(normalized)) risks.push("filesystem root");
3741
- return risks;
3724
+ return normalized === "/" || normalized === "\\" || /^[A-Za-z]:[\\/]*$/.test(normalized) ? ["filesystem root"] : [];
3742
3725
  }
3743
3726
  function isPrivateNetworkHostPattern(value) {
3744
3727
  const wildcardStripped = value.toLowerCase().replace(/^\*\./, "");
@@ -3750,8 +3733,12 @@ function isPrivateNetworkHostPattern(value) {
3750
3733
  "corp",
3751
3734
  "private"
3752
3735
  ].some((suffix) => wildcardStripped === suffix || wildcardStripped.endsWith(`.${suffix}`))) return true;
3753
- if (wildcardStripped.startsWith("10.") || wildcardStripped.startsWith("127.") || wildcardStripped.startsWith("192.168.") || wildcardStripped.startsWith("169.254.")) return true;
3754
- return /^172\.(1[6-9]|2\d|3[0-1])\./.test(wildcardStripped);
3736
+ return [
3737
+ "10.",
3738
+ "127.",
3739
+ "192.168.",
3740
+ "169.254."
3741
+ ].some((prefix) => wildcardStripped.startsWith(prefix)) || /^172\.(1[6-9]|2\d|3[0-1])\./.test(wildcardStripped);
3755
3742
  }
3756
3743
  function approvalResolutionMessage(outcome) {
3757
3744
  return {
@@ -3762,15 +3749,11 @@ function approvalResolutionMessage(outcome) {
3762
3749
  denied: "Codex app-server approval denied."
3763
3750
  }[outcome];
3764
3751
  }
3765
- function approvalEventScope(method, outcome) {
3766
- return method === "item/permissions/requestApproval" ? { scope: outcome === "approved-session" ? "session" : "turn" } : {};
3767
- }
3768
3752
  function readPolicyCommand(record) {
3769
3753
  const command = record?.command;
3770
3754
  if (typeof command === "string") return command;
3771
3755
  if (Array.isArray(command) && command.every((part) => typeof part === "string")) return command.join(" ");
3772
- const actionCommands = readCommandActions(record);
3773
- if (actionCommands.length > 0) return actionCommands.join(" && ");
3756
+ return readCommandActions(record).join(" && ") || void 0;
3774
3757
  }
3775
3758
  function readNetworkApprovalContext(record) {
3776
3759
  const context = isJsonObject(record?.networkApprovalContext) ? record.networkApprovalContext : void 0;
@@ -3786,22 +3769,16 @@ function readCommandActions(record) {
3786
3769
  if (!Array.isArray(actions)) return [];
3787
3770
  return actions.map((action) => isJsonObject(action) ? readStringField(action, "command") : void 0).filter((command) => Boolean(command));
3788
3771
  }
3789
- function readCommandActionsPreview(record) {
3790
- let source;
3791
- for (const command of readCommandActions(record)) {
3792
- source = appendPreviewPart(source, command, " && ");
3793
- if (source.clipped) break;
3794
- }
3795
- return source;
3796
- }
3797
3772
  function readCommandPreview(record) {
3798
3773
  const command = record?.command;
3799
3774
  if (typeof command === "string") return previewSource(command);
3800
- if (!Array.isArray(command)) return;
3775
+ return Array.isArray(command) ? readJoinedPreview(command, " ") : void 0;
3776
+ }
3777
+ function readJoinedPreview(parts, separator) {
3801
3778
  let source;
3802
- for (const part of command) {
3779
+ for (const part of parts) {
3803
3780
  if (typeof part !== "string") return;
3804
- source = appendPreviewPart(source, part, " ");
3781
+ source = previewSource(`${source?.value ? `${source.value}${separator}` : ""}${part}`);
3805
3782
  if (source.clipped) break;
3806
3783
  }
3807
3784
  return source;
@@ -3816,14 +3793,6 @@ function previewSource(value) {
3816
3793
  clipped: value.length > APPROVAL_PREVIEW_SCAN_MAX_LENGTH
3817
3794
  };
3818
3795
  }
3819
- function appendPreviewPart(source, part, separator) {
3820
- const value = `${source?.value ? `${source.value}${separator}` : ""}${part}`;
3821
- const clipped = source?.clipped === true || value.length > APPROVAL_PREVIEW_SCAN_MAX_LENGTH;
3822
- return {
3823
- value: sliceUtf16Safe(value, 0, APPROVAL_PREVIEW_SCAN_MAX_LENGTH),
3824
- clipped
3825
- };
3826
- }
3827
3796
  function sanitizeApprovalPreview(source, maxLength) {
3828
3797
  if (!source || !source.value) return { omitted: false };
3829
3798
  const rawPreview = stripDanglingCodexApprovalTerminalSequence(source.value);
@@ -3865,45 +3834,49 @@ function diagnosticToolIdentity(params) {
3865
3834
  toolCallId: params.call.callId
3866
3835
  };
3867
3836
  }
3868
- function emitDynamicToolStartedDiagnostic(params) {
3869
- emitTrustedDiagnosticEvent({
3870
- type: "tool.execution.started",
3871
- ...diagnosticToolIdentity(params)
3872
- });
3873
- }
3874
- function emitDynamicToolErrorDiagnostic(params) {
3875
- emitTrustedDiagnosticEvent({
3876
- type: "tool.execution.error",
3877
- ...diagnosticToolIdentity(params),
3878
- durationMs: params.durationMs,
3879
- errorCategory: "codex_dynamic_tool_error",
3880
- terminalReason: params.terminalReason ?? "failed"
3881
- });
3882
- }
3883
- /** Emits the terminal event matching a dynamic tool response's diagnostic type. */
3884
- function emitDynamicToolTerminalDiagnostic(params) {
3885
- const terminalType = params.response.diagnosticTerminalType ?? (params.response.success ? "completed" : "error");
3886
- if (terminalType === "completed") {
3887
- emitTrustedDiagnosticEvent({
3888
- type: "tool.execution.completed",
3889
- ...diagnosticToolIdentity(params),
3890
- durationMs: params.durationMs
3891
- });
3892
- return;
3893
- }
3894
- if (terminalType === "blocked") {
3837
+ function createCodexDynamicToolDiagnostics(params) {
3838
+ const matchesTerminal = (event) => {
3839
+ if (event.type !== "tool.execution.completed" && event.type !== "tool.execution.error" && event.type !== "tool.execution.blocked" || event.toolCallId !== params.call.callId || event.toolName !== params.call.tool) return false;
3840
+ if (params.runId !== void 0) return event.runId === params.runId;
3841
+ if (params.sessionId !== void 0) return event.sessionId === params.sessionId;
3842
+ if (params.sessionKey !== void 0) return event.sessionKey === params.sessionKey;
3843
+ return event.runId === void 0 && event.sessionId === void 0 && event.sessionKey === void 0;
3844
+ };
3845
+ const error = (durationMs, terminalReason = "failed") => {
3895
3846
  emitTrustedDiagnosticEvent({
3896
- type: "tool.execution.blocked",
3847
+ type: "tool.execution.error",
3897
3848
  ...diagnosticToolIdentity(params),
3898
- deniedReason: "plugin-before-tool-call",
3899
- reason: "Tool call blocked"
3849
+ durationMs,
3850
+ errorCategory: "codex_dynamic_tool_error",
3851
+ terminalReason
3900
3852
  });
3901
- return;
3902
- }
3903
- emitDynamicToolErrorDiagnostic({
3904
- ...params,
3905
- terminalReason: params.response.diagnosticTerminalReason ?? "failed"
3906
- });
3853
+ };
3854
+ return {
3855
+ matchesTerminal,
3856
+ hasPendingTerminal: () => hasPendingInternalDiagnosticEvent(matchesTerminal),
3857
+ started() {
3858
+ emitTrustedDiagnosticEvent({
3859
+ type: "tool.execution.started",
3860
+ ...diagnosticToolIdentity(params)
3861
+ });
3862
+ },
3863
+ error,
3864
+ terminal(response, durationMs) {
3865
+ const type = response.diagnosticTerminalType ?? (response.success ? "completed" : "error");
3866
+ if (type === "completed") emitTrustedDiagnosticEvent({
3867
+ type: "tool.execution.completed",
3868
+ ...diagnosticToolIdentity(params),
3869
+ durationMs
3870
+ });
3871
+ else if (type === "blocked") emitTrustedDiagnosticEvent({
3872
+ type: "tool.execution.blocked",
3873
+ ...diagnosticToolIdentity(params),
3874
+ deniedReason: "plugin-before-tool-call",
3875
+ reason: "Tool call blocked"
3876
+ });
3877
+ else error(durationMs, response.diagnosticTerminalReason ?? "failed");
3878
+ }
3879
+ };
3907
3880
  }
3908
3881
  //#endregion
3909
3882
  //#region extensions/codex/src/app-server/elicitation-bridge.ts
@@ -4051,7 +4024,8 @@ function resolvePluginElicitation(params) {
4051
4024
  const context = params.pluginAppPolicyContext;
4052
4025
  const entries = context ? Object.values(context.apps) : [];
4053
4026
  const pluginEntries = entries.filter(isPluginAppPolicyContextEntry);
4054
- const appId = readFirstString(meta, PLUGIN_APP_ID_META_KEYS) ?? readFirstString(requestParams, PLUGIN_APP_ID_META_KEYS);
4027
+ const readIdentity = (keys) => readFirstString(meta, keys) ?? readFirstString(requestParams, keys);
4028
+ const appId = readIdentity(PLUGIN_APP_ID_META_KEYS);
4055
4029
  const connectorId = readFirstString(meta, PLUGIN_CONNECTOR_ID_META_KEYS);
4056
4030
  const isCodexConnectorApproval = isCodexConnectorApprovalElicitation(requestParams, meta);
4057
4031
  if (isCodexConnectorApproval && appId && connectorId && codexAppIdentityKey(appId) !== codexAppIdentityKey(connectorId)) return {
@@ -4076,9 +4050,9 @@ function resolvePluginElicitation(params) {
4076
4050
  const matches = entries.filter((entry) => entry.mcpServerNames.includes(serverName));
4077
4051
  if (matches.length > 0) return uniquePluginMatch(matches, "server_name");
4078
4052
  }
4079
- const pluginName = readFirstString(meta, PLUGIN_NAME_META_KEYS) ?? readFirstString(requestParams, PLUGIN_NAME_META_KEYS);
4080
- const configKey = readFirstString(meta, PLUGIN_CONFIG_KEY_META_KEYS) ?? readFirstString(requestParams, PLUGIN_CONFIG_KEY_META_KEYS);
4081
- const marketplaceName = readFirstString(meta, PLUGIN_MARKETPLACE_NAME_META_KEYS) ?? readFirstString(requestParams, PLUGIN_MARKETPLACE_NAME_META_KEYS);
4053
+ const pluginName = readIdentity(PLUGIN_NAME_META_KEYS);
4054
+ const configKey = readIdentity(PLUGIN_CONFIG_KEY_META_KEYS);
4055
+ const marketplaceName = readIdentity(PLUGIN_MARKETPLACE_NAME_META_KEYS);
4082
4056
  if (pluginName || configKey) {
4083
4057
  if (!context) return {
4084
4058
  kind: "decline",
@@ -4173,31 +4147,19 @@ function readApprovalElicitation(requestParams, source) {
4173
4147
  meta,
4174
4148
  ...source.kind !== "computer-use" ? {
4175
4149
  persistHintsMode: "explicit",
4176
- allowedDecisions: buildApprovalAllowedDecisions(requestedSchema, meta, source.kind === "mcp")
4150
+ allowedDecisions: canMapPersistentApproval(requestedSchema, meta, source.kind === "mcp") ? [
4151
+ "allow-once",
4152
+ "allow-always",
4153
+ "deny"
4154
+ ] : ["allow-once", "deny"]
4177
4155
  } : {}
4178
4156
  };
4179
4157
  }
4180
- function buildApprovalAllowedDecisions(requestedSchema, meta, allowSession = false) {
4181
- return canMapPersistentApproval(requestedSchema, meta, allowSession) ? [
4182
- "allow-once",
4183
- "allow-always",
4184
- "deny"
4185
- ] : ["allow-once", "deny"];
4186
- }
4187
4158
  function canMapPersistentApproval(requestedSchema, meta, allowSession) {
4188
4159
  const persistHints = readPersistHints(meta, "explicit");
4189
4160
  if (allowSession) return choosePersistHint(persistHints) !== void 0;
4190
4161
  if (persistHints.length > 0) return persistHints.includes("always");
4191
- const properties = isJsonObject(requestedSchema.properties) ? requestedSchema.properties : {};
4192
- return Object.entries(properties).some(([name, value]) => {
4193
- const schema = isJsonObject(value) ? value : void 0;
4194
- if (!schema) return false;
4195
- return isPersistField({
4196
- name,
4197
- schema,
4198
- required: false
4199
- }) && chooseAlwaysPersistOptionValue(readEnumOptions(schema)) !== void 0;
4200
- });
4162
+ return readApprovalProperties(isJsonObject(requestedSchema.properties) ? requestedSchema.properties : {}).some((property) => isPersistField(property) && chooseAlwaysPersistOptionValue(readEnumOptions(property.schema)) !== void 0);
4201
4163
  }
4202
4164
  function logPluginElicitationDecline(reason, requestParams) {
4203
4165
  embeddedAgentLog.debug("codex plugin elicitation declined", {
@@ -4227,25 +4189,26 @@ function buildApprovalDescription(params) {
4227
4189
  ].filter(Boolean).join("\n\n");
4228
4190
  }
4229
4191
  function readPropertyDescriptionLines(requestedSchema) {
4230
- const properties = isJsonObject(requestedSchema.properties) ? requestedSchema.properties : {};
4231
- return Object.entries(properties).map(([name, value]) => {
4232
- const schema = isJsonObject(value) ? value : void 0;
4233
- if (!schema) return;
4192
+ return readApprovalProperties(isJsonObject(requestedSchema.properties) ? requestedSchema.properties : {}).map(({ name, schema }) => {
4234
4193
  const propTitle = sanitizeDisplayText(readNonBlankString(schema.title) ?? "") || sanitizeDisplayText(name) || "field";
4235
4194
  const description = sanitizeOptionalDisplayText(readNonBlankString(schema.description));
4236
4195
  return description ? `- ${propTitle}: ${description}` : `- ${propTitle}`;
4237
- }).filter((line) => Boolean(line));
4196
+ });
4197
+ }
4198
+ function readApprovalProperties(properties) {
4199
+ return Object.entries(properties).flatMap(([name, schema]) => isJsonObject(schema) ? [{
4200
+ name,
4201
+ schema
4202
+ }] : []);
4238
4203
  }
4239
4204
  function readDisplayParamLines(meta) {
4240
4205
  const displayParams = meta[MCP_TOOL_APPROVAL_TOOL_PARAMS_DISPLAY_KEY];
4241
4206
  if (!Array.isArray(displayParams)) return [];
4242
- const lines = displayParams.slice(0, MAX_DISPLAY_PARAM_ENTRIES).map((entry) => {
4243
- const param = isJsonObject(entry) ? entry : void 0;
4244
- if (!param) return;
4207
+ const lines = displayParams.slice(0, MAX_DISPLAY_PARAM_ENTRIES).flatMap((param) => {
4208
+ if (!isJsonObject(param)) return [];
4245
4209
  const name = sanitizeOptionalDisplayText(readNonBlankString(param.display_name)) ?? sanitizeOptionalDisplayText(readNonBlankString(param.name));
4246
- if (!name) return;
4247
- return `- ${name}: ${formatDisplayParamValue(param.value)}`;
4248
- }).filter((line) => Boolean(line));
4210
+ return name ? [`- ${name}: ${formatDisplayParamValue(param.value)}`] : [];
4211
+ });
4249
4212
  const remaining = displayParams.length - MAX_DISPLAY_PARAM_ENTRIES;
4250
4213
  return remaining > 0 ? [...lines, `- Additional parameters: ${remaining} more`] : lines;
4251
4214
  }
@@ -4268,19 +4231,15 @@ function formatDisplayJsonValue(value, depth = MAX_DISPLAY_VALUE_DEPTH) {
4268
4231
  if (typeof value === "object") {
4269
4232
  if (depth <= 0) return "{truncated}";
4270
4233
  const parts = [];
4271
- let count = 0;
4272
- let truncated = false;
4273
4234
  for (const key in value) {
4274
4235
  if (!Object.hasOwn(value, key)) continue;
4275
- if (count >= MAX_DISPLAY_VALUE_OBJECT_KEYS) {
4276
- truncated = true;
4236
+ if (parts.length >= MAX_DISPLAY_VALUE_OBJECT_KEYS) {
4237
+ parts.push("...");
4277
4238
  break;
4278
4239
  }
4279
4240
  const safeKey = truncateCodexApprovalDisplayText(sanitizeDisplayText(key), 80);
4280
4241
  parts.push(`${JSON.stringify(safeKey)}:${formatDisplayJsonValue(value[key] ?? null, depth - 1)}`);
4281
- count += 1;
4282
4242
  }
4283
- if (truncated) parts.push("...");
4284
4243
  return `{${parts.join(",")}}`;
4285
4244
  }
4286
4245
  return "null";
@@ -4318,41 +4277,28 @@ function buildAcceptedContent(approvalPrompt, outcome) {
4318
4277
  const content = {};
4319
4278
  let sawApprovalField = false;
4320
4279
  const persist = choosePersistHint(readPersistHints(meta, approvalPrompt.persistHintsMode));
4321
- for (const [name, value] of Object.entries(properties)) {
4322
- const schema = isJsonObject(value) ? value : void 0;
4323
- if (!schema) continue;
4324
- const property = {
4325
- name,
4326
- schema,
4327
- required: required.has(name)
4328
- };
4329
- const next = readApprovalFieldValue(property, outcome, persist) ?? readPersistFieldValue(property, meta, outcome, approvalPrompt.persistHintsMode ?? "legacy") ?? (outcome === "approved-once" && isPersistField(property) ? void 0 : property.schema.default);
4280
+ for (const property of readApprovalProperties(properties)) {
4281
+ const next = readAcceptedPropertyValue(property, outcome, persist, approvalPrompt.persistHintsMode ?? "legacy");
4330
4282
  if (isApprovalField(property)) sawApprovalField = true;
4331
4283
  if (next === void 0) {
4332
- if (property.required) return;
4284
+ if (required.has(property.name)) return;
4333
4285
  continue;
4334
4286
  }
4335
- content[name] = next;
4287
+ content[property.name] = next;
4336
4288
  }
4337
4289
  return sawApprovalField ? content : void 0;
4338
4290
  }
4339
- function readApprovalFieldValue(property, outcome, persist) {
4340
- if (!isApprovalField(property)) return;
4341
- if (readNonBlankString(property.schema.type) === "boolean") return true;
4342
- const options = readEnumOptions(property.schema);
4343
- if (options.length === 0) return;
4344
- const acceptChoice = options.find((option) => isPositiveApprovalOption(option));
4345
- if (outcome === "approved-session") return options.find((option) => isPersistentApprovalOption(option, persist))?.value ?? acceptChoice?.value;
4346
- return acceptChoice?.value;
4347
- }
4348
- function readPersistFieldValue(property, meta, outcome, persistHintsMode) {
4349
- if (!isPersistField(property) || outcome !== "approved-session") return;
4350
- const persistHints = readPersistHints(meta, persistHintsMode);
4291
+ function readAcceptedPropertyValue(property, outcome, persist, persistHintsMode) {
4292
+ if (isApprovalField(property)) {
4293
+ if (readNonBlankString(property.schema.type) === "boolean") return true;
4294
+ const options = readEnumOptions(property.schema);
4295
+ const choice = (outcome === "approved-session" ? options.find((option) => isPersistentApprovalOption(option, persist)) : void 0) ?? options.find(isPositiveApprovalOption);
4296
+ if (choice) return choice.value;
4297
+ }
4298
+ if (!isPersistField(property)) return property.schema.default;
4299
+ if (outcome === "approved-once") return;
4351
4300
  const options = readEnumOptions(property.schema);
4352
- if (options.length === 0) return;
4353
- const preferred = choosePersistHint(persistHints);
4354
- if (preferred) return options.find((option) => option.value === preferred || option.label === preferred)?.value;
4355
- if (persistHintsMode === "explicit") return chooseAlwaysPersistOptionValue(options);
4301
+ return (persist ? options.find((option) => option.value === persist || option.label === persist)?.value : persistHintsMode === "explicit" ? chooseAlwaysPersistOptionValue(options) : void 0) ?? property.schema.default;
4356
4302
  }
4357
4303
  function isApprovalField(property) {
4358
4304
  const haystack = propertyText(property).toLowerCase();
@@ -4381,14 +4327,10 @@ function buildAcceptedMeta(meta, outcome, persistHintsMode) {
4381
4327
  return persist ? { persist } : null;
4382
4328
  }
4383
4329
  function choosePersistHint(persistHints) {
4384
- if (persistHints.includes("always")) return "always";
4385
- if (persistHints.includes("session")) return "session";
4330
+ return ["always", "session"].find((hint) => persistHints.includes(hint));
4386
4331
  }
4387
4332
  function chooseAlwaysPersistOptionValue(options) {
4388
- return options.find((option) => optionMatchesPersist(option, "always"))?.value;
4389
- }
4390
- function optionMatchesPersist(option, persist) {
4391
- return option.value.toLowerCase() === persist || option.label.toLowerCase() === persist;
4333
+ return options.find((option) => option.value.toLowerCase() === "always" || option.label.toLowerCase() === "always")?.value;
4392
4334
  }
4393
4335
  function hasNoSchemaProperties(requestedSchema) {
4394
4336
  const properties = isJsonObject(requestedSchema.properties) ? requestedSchema.properties : {};
@@ -4403,15 +4345,14 @@ function readEnumOptions(schema) {
4403
4345
  label: labels[index] ?? value
4404
4346
  }));
4405
4347
  }
4406
- if (Array.isArray(schema.oneOf)) return schema.oneOf.map((entry) => {
4348
+ if (Array.isArray(schema.oneOf)) return schema.oneOf.flatMap((entry) => {
4407
4349
  const option = isJsonObject(entry) ? entry : void 0;
4408
4350
  const value = readNonBlankString(option?.const);
4409
- if (!value) return;
4410
- return {
4351
+ return value ? [{
4411
4352
  value,
4412
4353
  label: readNonBlankString(option?.title) ?? value
4413
- };
4414
- }).filter((entry) => Boolean(entry));
4354
+ }] : [];
4355
+ });
4415
4356
  return [];
4416
4357
  }
4417
4358
  function isPositiveApprovalOption(option) {
@@ -4563,7 +4504,7 @@ function projectCodexDynamicTools(tools) {
4563
4504
  continue;
4564
4505
  }
4565
4506
  const descriptor = readCodexDynamicToolDescriptor(tool, toolIndex);
4566
- if (!descriptor.ok) {
4507
+ if ("diagnostic" in descriptor) {
4567
4508
  quarantinedTools.push(descriptor.diagnostic);
4568
4509
  continue;
4569
4510
  }
@@ -4597,13 +4538,10 @@ function projectCodexDynamicTools(tools) {
4597
4538
  }
4598
4539
  function readCodexDynamicToolDescriptor(tool, toolIndex) {
4599
4540
  const fallbackName = `tool[${toolIndex}]`;
4600
- const invalid = (name, violation) => ({
4601
- ok: false,
4602
- diagnostic: {
4603
- tool: name,
4604
- violations: [`${name}.${violation}`]
4605
- }
4606
- });
4541
+ const invalid = (name, violation) => ({ diagnostic: {
4542
+ tool: name,
4543
+ violations: [`${name}.${violation}`]
4544
+ } });
4607
4545
  let name;
4608
4546
  try {
4609
4547
  const rawName = tool.name;
@@ -4633,7 +4571,6 @@ function readCodexDynamicToolDescriptor(tool, toolIndex) {
4633
4571
  return invalid(name, "inputSchema is unreadable");
4634
4572
  }
4635
4573
  return {
4636
- ok: true,
4637
4574
  name,
4638
4575
  description,
4639
4576
  parameters
@@ -4785,6 +4722,39 @@ function projectCodexExecutableDynamicToolSurface(tools, hookContext) {
4785
4722
  };
4786
4723
  }
4787
4724
  //#endregion
4725
+ //#region extensions/codex/src/app-server/dynamic-tool-source-reply.ts
4726
+ /**
4727
+ * Resolves the source-reply facts of one Codex dynamic tool result and records whether
4728
+ * it ends the Codex turn on `response`. A final reply
4729
+ * authored by a `canDeliverSourceReply` tool, read from the result after middleware and
4730
+ * extensions, is appended to `payloads`; the host delivers it and writes its transcript
4731
+ * row after the send. Only calls in the model-only namespace qualify: Codex never
4732
+ * exposes that namespace to Code Mode programs, so a program's intermediate call
4733
+ * cannot end the turn or reach the conversation.
4734
+ */
4735
+ function resolveCodexToolResultSourceReply(params) {
4736
+ const messageToolOnly = params.sourceReplyDeliveryMode === "message_tool_only" && params.toolName === "message";
4737
+ const toolConfirmed = messageToolOnly && !params.resultIsError && (params.rawResult.terminate === true || params.result.terminate === true);
4738
+ const confirmed = messageToolOnly && (toolConfirmed || params.deliveredSourceReply);
4739
+ const final = confirmed ? params.executedArgs.final !== false : void 0;
4740
+ const payload = params.canDeliverSourceReply === true && !params.resultIsError && params.call.namespace === "openclaw_direct" ? captureToolAuthoredSourceReply({
4741
+ result: params.result,
4742
+ toolCallId: params.call.callId,
4743
+ idempotencyScope: params.runId ?? params.call.turnId
4744
+ }) : void 0;
4745
+ if (payload) params.payloads.push(payload);
4746
+ const toolAuthoredFinal = Boolean(payload);
4747
+ const continuesSourceReplyProgress = confirmed && final === false;
4748
+ const terminate = toolAuthoredFinal || (params.rawResult.terminate === true || params.result.terminate === true) && !continuesSourceReplyProgress || [params.rawResult, params.result].some(({ details }) => isRecord(details) && typeof details.status === "string" && details.status.trim().toLowerCase() === "yielded") || confirmed && final === true || void 0;
4749
+ params.response.terminate = terminate;
4750
+ if (toolAuthoredFinal) params.response.toolAuthoredFinalReply = true;
4751
+ return {
4752
+ toolConfirmed,
4753
+ final,
4754
+ terminate
4755
+ };
4756
+ }
4757
+ //#endregion
4788
4758
  //#region extensions/codex/src/app-server/dynamic-tools.ts
4789
4759
  const MAX_CODEX_DYNAMIC_TOOL_VALIDATION_ERRORS = 4;
4790
4760
  const MAX_CODEX_DYNAMIC_TOOL_VALIDATION_ERROR_CHARS = 160;
@@ -4821,11 +4791,6 @@ function computerFrameImageIdentity(content) {
4821
4791
  const image = expectDefined(images[0], "single Codex computer frame image");
4822
4792
  return createHash("sha256").update(JSON.stringify([image.mimeType, image.data])).digest("hex");
4823
4793
  }
4824
- function invalidateComputerFrame(contextEpoch) {
4825
- contextEpoch.value += 1;
4826
- delete contextEpoch.frameToolCallId;
4827
- delete contextEpoch.frameImageIdentity;
4828
- }
4829
4794
  function createCodexDynamicToolBridge(params) {
4830
4795
  const toolResultHookContext = toToolResultHookContext(params.hookContext);
4831
4796
  const contextWindowTokens = params.hookContext?.contextWindowTokens;
@@ -5089,7 +5054,7 @@ function createCodexDynamicToolBridge(params) {
5089
5054
  const contentItems = convertToolContents(result.content, toolResultMaxChars);
5090
5055
  const deliveredFrameImages = contentItems.filter((item) => item.type === "inputImage");
5091
5056
  const finalFrameImageIdentity = computerFrameImageIdentity(result.content);
5092
- if (toolName === "computer" && params.computerContextEpoch?.frameToolCallId === call.callId && (deliveredFrameImages.length !== 1 || finalFrameImageIdentity === void 0 || finalFrameImageIdentity !== params.computerContextEpoch.frameImageIdentity)) invalidateComputerFrame(params.computerContextEpoch);
5057
+ if (toolName === "computer" && params.computerContextEpoch?.frameToolCallId === call.callId && (deliveredFrameImages.length !== 1 || finalFrameImageIdentity === void 0 || finalFrameImageIdentity !== params.computerContextEpoch.frameImageIdentity)) invalidateCodexComputerFrame(params.computerContextEpoch);
5093
5058
  const response = {
5094
5059
  contentItems,
5095
5060
  success: !resultIsError,
@@ -5097,9 +5062,20 @@ function createCodexDynamicToolBridge(params) {
5097
5062
  diagnosticTerminalReason: resultFailureKind === "blocked" ? void 0 : resultFailureKind,
5098
5063
  transcriptDetails: asOptionalRecord(sanitizeToolResult(result))?.details
5099
5064
  };
5100
- const toolConfirmedSourceReply = params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && !resultIsError && (rawResult.terminate === true || result.terminate === true);
5101
- const confirmedSourceReply = params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && (toolConfirmedSourceReply || deliveredSourceReply);
5102
- const sourceReplyFinal = confirmedSourceReply ? executedArgs.final !== false : void 0;
5065
+ const sourceReply = resolveCodexToolResultSourceReply({
5066
+ sourceReplyDeliveryMode: params.hookContext?.sourceReplyDeliveryMode,
5067
+ canDeliverSourceReply: toolEntry.tool.canDeliverSourceReply,
5068
+ toolName,
5069
+ call,
5070
+ resultIsError,
5071
+ rawResult,
5072
+ result,
5073
+ deliveredSourceReply,
5074
+ executedArgs,
5075
+ runId: toolResultHookContext.runId,
5076
+ payloads: telemetry.messagingToolSourceReplyPayloads,
5077
+ response
5078
+ });
5103
5079
  const autoDeliveryTtsMediaUrls = getCoreTtsToolResultMediaUrls(rawResult);
5104
5080
  recordAgentHarnessToolResultTelemetry({
5105
5081
  extractSourceReplyPayload: extractMessagingToolSourceReplyPayload,
@@ -5117,12 +5093,10 @@ function createCodexDynamicToolBridge(params) {
5117
5093
  autoDeliveryTtsMediaUrls,
5118
5094
  coreTtsToolResult: autoDeliveryTtsMediaUrls?.length ? rawResult : void 0,
5119
5095
  messagingTarget: confirmedMessagingTarget,
5120
- sourceReplyFinal,
5096
+ sourceReplyFinal: sourceReply.final,
5121
5097
  trustedLocalMediaToolNames: pluginLocalMediaTrustByToolName.get(toolName)
5122
5098
  });
5123
- if (deliveredSourceReply || toolConfirmedSourceReply) telemetry.didDeliverSourceReplyViaMessageTool = true;
5124
- const continuesSourceReplyProgress = confirmedSourceReply && sourceReplyFinal === false;
5125
- response.terminate = (rawResult.terminate === true || result.terminate === true) && !continuesSourceReplyProgress || isToolResultYield(rawResult) || isToolResultYield(result) || confirmedSourceReply && sourceReplyFinal === true || void 0;
5099
+ if (deliveredSourceReply || sourceReply.toolConfirmed) telemetry.didDeliverSourceReplyViaMessageTool = true;
5126
5100
  const asyncStarted = isAsyncStartedToolResult(rawResult) || isAsyncStartedToolResult(result);
5127
5101
  response.asyncStarted = asyncStarted || void 0;
5128
5102
  const replaySafe = executionBoundary.executionPrevented || !asyncStarted && isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs);
@@ -5134,7 +5108,7 @@ function createCodexDynamicToolBridge(params) {
5134
5108
  return response;
5135
5109
  },
5136
5110
  onError: ({ error, boundary: executionBoundary, executedArguments: executedArgs, startedAt }) => {
5137
- if (toolName === "computer" && params.computerContextEpoch?.frameToolCallId === call.callId) invalidateComputerFrame(params.computerContextEpoch);
5111
+ if (toolName === "computer" && params.computerContextEpoch?.frameToolCallId === call.callId) invalidateCodexComputerFrame(params.computerContextEpoch);
5138
5112
  const executionDisposition = getBeforeToolCallFailureDisposition(error) ?? (signal.aborted ? resolveCodexToolAbortTerminalReason(signal) : resolveToolExecutionErrorKind(error));
5139
5113
  const errorMessage = formatToolExecutionErrorMessage(error, "OpenClaw dynamic tool call failed.");
5140
5114
  const operatorHint = readToolOperatorHint(error);
@@ -5213,10 +5187,12 @@ function reportQuarantinedDynamicTools(params) {
5213
5187
  });
5214
5188
  }
5215
5189
  function dedupeQuarantinedDynamicTools(tools) {
5216
- return [...new Map(tools.map((tool) => [tool.tool, {
5217
- tool: tool.tool,
5218
- violations: tool.violations
5219
- }])).values()];
5190
+ const byName = /* @__PURE__ */ new Map();
5191
+ for (const { tool, violations } of tools) byName.set(tool, {
5192
+ tool,
5193
+ violations
5194
+ });
5195
+ return [...byName.values()];
5220
5196
  }
5221
5197
  function toToolResultHookContext(ctx) {
5222
5198
  const { agentId, sessionId, sessionKey, runId, channelId } = ctx ?? {};
@@ -5228,14 +5204,9 @@ function toToolResultHookContext(ctx) {
5228
5204
  ...channelId && { channelId }
5229
5205
  };
5230
5206
  }
5231
- function isToolResultYield(result) {
5232
- const details = result.details;
5233
- if (!isRecord(details) || typeof details.status !== "string") return false;
5234
- return details.status.trim().toLowerCase() === "yielded";
5235
- }
5236
5207
  function isAsyncStartedToolResult(result) {
5237
5208
  const details = result.details;
5238
5209
  return isRecord(details) && details.async === true && details.status === "started";
5239
5210
  }
5240
5211
  //#endregion
5241
- export { emitDynamicToolStartedDiagnostic as a, ensureCodexSandboxExecServerEnvironment as c, CodexNativeProcessAuthority as d, readCodexRetainedBackgroundCommands as f, emitDynamicToolErrorDiagnostic as i, releaseCodexSandboxExecServerEnvironment as l, CodexNativeToolLifecycleProjector as m, projectCodexExecutableDynamicTools as n, emitDynamicToolTerminalDiagnostic as o, createCodexRemoteWorkspaceFileReader as p, routeCodexAppServerElicitationRequest as r, handleCodexAppServerApprovalRequest as s, createCodexDynamicToolBridge as t, createCodexNativePreToolUseFailureBuffer as u };
5212
+ export { invalidateCodexComputerFrame as _, handleCodexAppServerApprovalRequest as a, createCodexNativePreToolUseFailureBuffer as c, buildCodexTemporalAdditionalContext as d, buildTurnCollaborationMode as f, buildCodexUserInput as g, readCodexRetainedBackgroundCommands as h, createCodexDynamicToolDiagnostics as i, buildCodexHistoryProvenancePrefix as l, CodexNativeProcessAuthority as m, projectCodexExecutableDynamicTools as n, ensureCodexSandboxExecServerEnvironment as o, buildTurnStartParams as p, routeCodexAppServerElicitationRequest as r, releaseCodexSandboxExecServerEnvironment as s, createCodexDynamicToolBridge as t, buildCodexParentLocalInstructions as u, createCodexRemoteWorkspaceFileReader as v, CodexNativeToolLifecycleProjector as y };