@openclaw/codex 2026.9.4 → 2026.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/assets/activity.svg +30 -0
  2. package/assets/icon.png +0 -0
  3. package/dist/.setup/account-usage-runtime-Eem_WGqY.mjs +75 -0
  4. package/dist/{apply-CvT8tW5i.js → .setup/apply-DDOi8uNx.mjs} +24 -30
  5. package/dist/.setup/async-questions-DdhOCwGi.mjs +27 -0
  6. package/dist/{attempt-client-cleanup-DKHzbwt5.js → .setup/attempt-client-cleanup-CEv1cKwF.mjs} +79 -49
  7. package/dist/.setup/attempt-terminal-gZrxQ35N.mjs +5 -0
  8. package/dist/.setup/attempt-workspace-context-DUVEn_CS.mjs +331 -0
  9. package/dist/{auth-binding-CWlKVccJ.js → .setup/auth-binding-DhLWa_7K.mjs} +1 -1
  10. package/dist/{auth-cache-key-7orQae_j.js → .setup/auth-cache-key-B2mpaI9D.mjs} +9 -81
  11. package/dist/{auth-profile-CwzO4_RG.js → .setup/auth-profile-WqZtZfXN.mjs} +1 -1
  12. package/dist/.setup/auth-profile-health-oHizqgye.mjs +53 -0
  13. package/dist/.setup/auth-profile-recovery-BREezV0L.mjs +14 -0
  14. package/dist/.setup/binding-connection-ThoatDcb.mjs +50 -0
  15. package/dist/.setup/binding-connection.runtime-yoHrABt-.mjs +48 -0
  16. package/dist/{bounded-turn-C_Hneydj.js → .setup/bounded-turn-B2HScEgT.mjs} +143 -32
  17. package/dist/{build-state-BCmg9-5p.js → .setup/build-state-C7EnDVgr.mjs} +1 -1
  18. package/dist/{capabilities-8vx68WLH.js → .setup/capabilities-CDXOOFdZ.mjs} +1 -1
  19. package/dist/{client-B57KWPQx.js → .setup/client-Cs08OXVQ.mjs} +705 -512
  20. package/dist/.setup/client-catalog-response-BMgt_4q4.mjs +285 -0
  21. package/dist/{command-dispatch-BpduNGVf.js → .setup/command-dispatch-B4JuOsOk.mjs} +8 -4
  22. package/dist/{command-formatters-lxlkNZgU.js → .setup/command-formatters-Bmqvr9tO.mjs} +8 -4
  23. package/dist/{command-handlers-tiL9iWj7.js → .setup/command-handlers-DW7Lx9oH.mjs} +137 -186
  24. package/dist/{command-rpc-CR5EOEZX.js → .setup/command-rpc-DixIj3HW.mjs} +47 -23
  25. package/dist/{compact-B_H3AOd7.js → .setup/compact-Du5UvtxA.mjs} +29 -51
  26. package/dist/{config-oIORaQ5T.js → .setup/config-BoTP_mrL.mjs} +5 -4
  27. package/dist/{config-options-CXMq1T-C.js → .setup/config-options-BvaRs51b.mjs} +50 -446
  28. package/dist/{protocol-5bh1G-H7.js → .setup/config-parsing-CcB9iPoq.mjs} +9 -51
  29. package/dist/{config-runtime-D0zGNIHu.js → .setup/config-runtime-C8Rw1m1m.mjs} +2 -2
  30. package/dist/.setup/config-security-BEReZ6go.mjs +380 -0
  31. package/dist/{conversation-binding-DsB4-O7U.js → .setup/conversation-binding-DP-gMrBP.mjs} +29 -38
  32. package/dist/{conversation-binding-data-BiodpIzq.js → .setup/conversation-binding-data-CrcyZk6O.mjs} +1 -1
  33. package/dist/{conversation-control-CtUu1b3m.js → .setup/conversation-control-uK9Jomlc.mjs} +32 -67
  34. package/dist/.setup/cyber-failover-DYwbmf1o.mjs +123 -0
  35. package/dist/.setup/cyber-failover-notice-e9G3J9-e.mjs +17 -0
  36. package/dist/{dynamic-tools-CmN5RAip.js → .setup/dynamic-tools-BDXl-8Cw.mjs} +936 -620
  37. package/dist/{effective-mcp-catalog-Bd73bqA8.js → .setup/effective-mcp-catalog-CvnvnGIe.mjs} +3 -3
  38. package/dist/{ephemeral-turn-D78_VmzO.js → .setup/ephemeral-turn-poidXgek.mjs} +9 -7
  39. package/dist/{event-projector-assistant-message-BmBM4qBV.js → .setup/event-projector-assistant-message-BsruvNrH.mjs} +8 -7
  40. package/dist/{event-projector-items-5VhsECCO.js → .setup/event-projector-items-C6L_5ZiT.mjs} +42 -8
  41. package/dist/{attempt-notifications-C9XufG6l.js → .setup/event-projector-usage-CnBS8Wa3.mjs} +102 -9
  42. package/dist/{harness-C7rRa7bm.js → .setup/harness-a_MsE4-_.mjs} +81 -21
  43. package/dist/{inference-context-Dr_mzvk4.js → .setup/inference-context-D96l9-ew.mjs} +1 -1
  44. package/dist/.setup/inference-proxy-b-aJMSxm.mjs +601 -0
  45. package/dist/{isolated-completion-DWVb5U2J.js → .setup/isolated-completion-BIZFhTNG.mjs} +18 -11
  46. package/dist/.setup/launch-args-DbFCehO7.mjs +196 -0
  47. package/dist/{managed-binary-D22rNEcR.js → .setup/managed-binary-BnshlFag.mjs} +14 -3
  48. package/dist/{managed-thread-store-D1t5jh3G.js → .setup/managed-thread-store-BMqgThVL.mjs} +48 -14
  49. package/dist/{model-catalog-Dq2n8f_p.js → .setup/model-catalog-CfXJf9Ay.mjs} +36 -12
  50. package/dist/{models-PCKOim-h.js → .setup/models-KnqBv0hE.mjs} +3 -3
  51. package/dist/.setup/native-auth-rWbGbMn3.mjs +57 -0
  52. package/dist/{native-subagent-history-DjtPloaU.js → .setup/native-subagent-history-0nYYgo0k.mjs} +43 -35
  53. package/dist/.setup/native-subagent-monitor-D3filLY2.mjs +4859 -0
  54. package/dist/{native-subagent-task-ids-BzTdT0Uw.js → .setup/native-subagent-task-ids-wb8CKZGN.mjs} +98 -44
  55. package/dist/{node-cli-sessions-DKaOmVUh.js → .setup/node-cli-sessions-r3avj02l.mjs} +86 -68
  56. package/dist/.setup/node-exec-server.runtime-BDwWXVpK.mjs +247 -0
  57. package/dist/{plan-7uG-Fp36.js → .setup/plan-CN3VwfNU.mjs} +133 -119
  58. package/dist/{plugin-activation-BYQculil.js → .setup/plugin-activation-DT3MNtqj.mjs} +7 -16
  59. package/dist/{plugin-app-cache-key-d0eKxy7b.js → .setup/plugin-app-cache-key-B2CsSdnV.mjs} +32 -44
  60. package/dist/{plugin-approval-roundtrip-Bz3FT8W5.js → .setup/plugin-approval-roundtrip-C0MZzCuP.mjs} +1 -1
  61. package/dist/{plugin-inventory-oL4Na1ZG.js → .setup/plugin-inventory-BoRei8Z4.mjs} +21 -11
  62. package/dist/{plugin-marketplace-discovery-P-Vndfqq.js → .setup/plugin-marketplace-discovery-CDOi8IFs.mjs} +4 -1
  63. package/dist/{plugin-metadata-cache-ClPJpRio.js → .setup/plugin-metadata-cache-Ctmo83k_.mjs} +226 -141
  64. package/dist/.setup/protocol-CANUwXJ3.mjs +17 -0
  65. package/dist/{provider-MQ8PVkuE.js → .setup/provider-BOsxQHOM.mjs} +9 -5
  66. package/dist/{provider-capabilities-BvCUL69A.js → .setup/provider-capabilities-BfE88FZ5.mjs} +31 -3
  67. package/dist/{rate-limits-CBMmWBr9.js → .setup/rate-limits-CMU3DUD0.mjs} +174 -151
  68. package/dist/{request-B7hEkBGq.js → .setup/request-D2L0zMrq.mjs} +130 -42
  69. package/dist/{run-attempt-CvsAAGa0.js → .setup/run-attempt-BHu11r7h.mjs} +1628 -966
  70. package/dist/.setup/run-attempt-lifecycle-B7JbY4-F.mjs +71 -0
  71. package/dist/{runtime-artifact-BJ0rXBZ_.js → .setup/runtime-artifact-CLZ8udh-.mjs} +29 -45
  72. package/dist/{sandbox-guard-BxgyoM8M.js → .setup/sandbox-guard-C2sRMOEY.mjs} +3 -3
  73. package/dist/{session-binding-CI8UuilT.js → .setup/session-binding-Cm0apEbd.mjs} +36 -83
  74. package/dist/{session-binding-record-Bcvslnhf.js → .setup/session-binding-record-BGoz8wOK.mjs} +202 -5
  75. package/dist/.setup/session-binding-sidecars-BlB5EvIS.mjs +546 -0
  76. package/dist/.setup/session-catalog-Pno1xGcl.mjs +2764 -0
  77. package/dist/{session-catalog-adoption-tcZi1IfX.js → .setup/session-catalog-adoption-BI6UTDZT.mjs} +58 -26
  78. package/dist/{session-catalog-archive-CwKg8Xz2.js → .setup/session-catalog-archive-lHBqzhaO.mjs} +6 -6
  79. package/dist/.setup/session-catalog-events-Bj6j94E_.mjs +347 -0
  80. package/dist/.setup/session-catalog-index-B9_VQIUz.mjs +1170 -0
  81. package/dist/.setup/session-catalog-index-order-hcdz07yN.mjs +48 -0
  82. package/dist/.setup/session-catalog-index-state-Dgh8ORJm.mjs +183 -0
  83. package/dist/{thread-history-page-D0JIp48V.js → .setup/session-catalog-native-projection-DowriLid.mjs} +223 -95
  84. package/dist/{session-catalog-node-adoption-B1tgDW-m.js → .setup/session-catalog-node-adoption-ITddWx_X.mjs} +25 -13
  85. package/dist/{session-catalog-node-continue-Ci-DXyJP.js → .setup/session-catalog-node-continue-6g6qi4A9.mjs} +24 -15
  86. package/dist/.setup/session-catalog-projection-zK_PUsiE.mjs +422 -0
  87. package/dist/{session-cli-DNN_jhGc.js → .setup/session-cli-irrD-uo7.mjs} +3 -3
  88. package/dist/{session-history-Cz-tOOYj.js → .setup/session-history-BrqhSCf9.mjs} +19 -19
  89. package/dist/{session-history-import-DwyijFI6.js → .setup/session-history-import-D-3wAgOP.mjs} +4 -4
  90. package/dist/{session-history-read-cxPUxyVV.js → .setup/session-history-read-B3ZfSUbF.mjs} +2 -2
  91. package/dist/{session-history.worker-BeK1YOyT.js → .setup/session-history.worker-CaCh0xF_.mjs} +10 -24
  92. package/dist/{session-initialization-Ct0_Hzmg.js → .setup/session-initialization-rcgbZiid.mjs} +2 -2
  93. package/dist/{session-permission-policy-BBmXA8Eg.js → .setup/session-permission-policy-BI1QK_98.mjs} +2 -2
  94. package/dist/{session-retirement-B2YfbAAT.js → .setup/session-retirement-BH-S6qkJ.mjs} +26 -7
  95. package/dist/{settled-turn-context-Du6gdUDd.js → .setup/settled-turn-context-fmwouT5I.mjs} +2 -2
  96. package/dist/{settled-turn-finalizer-Dn91stmc.js → .setup/settled-turn-finalizer-DiAi6Kx6.mjs} +12 -12
  97. package/dist/{shared-client-CscigXXL.js → .setup/shared-client-DA4VR4Eb.mjs} +332 -221
  98. package/dist/{side-question-BGMJQf4U.js → .setup/side-question-BJ1wSDcB.mjs} +183 -230
  99. package/dist/{source-CElXU0Oy.js → .setup/source-CUzJq5WN.mjs} +11 -10
  100. package/dist/.setup/spawn-error-CL3n6MAa.mjs +57 -0
  101. package/dist/{thread-archive-guard-DfpBr-Sz.js → .setup/thread-archive-guard-C9frICzX.mjs} +10 -3
  102. package/dist/.setup/thread-history-page-DoVpnHim.mjs +83 -0
  103. package/dist/{thread-lifecycle-DXl1-I6b.js → .setup/thread-lifecycle-B3mfz1TG.mjs} +347 -2318
  104. package/dist/{thread-ownership-BsDRTsyp.js → .setup/thread-ownership-DcTtAcXj.mjs} +6 -4
  105. package/dist/{thread-policy-BuEQfE0g.js → .setup/thread-policy-DGuQsuFC.mjs} +19 -3
  106. package/dist/.setup/thread-requests-BLvGkP2R.mjs +2205 -0
  107. package/dist/{thread-resume-DsDOZpFg.js → .setup/thread-resume-DIBXwOpE.mjs} +8 -5
  108. package/dist/.setup/timeout-C910MdAB.mjs +80 -0
  109. package/dist/{transcript-history-projection-v17wXgdK.js → .setup/transcript-history-projection-ZZpkFkJ_.mjs} +96 -61
  110. package/dist/{transcript-mirror-C5CKgNy6.js → .setup/transcript-mirror-BtHDBp1G.mjs} +224 -195
  111. package/dist/{transcript-mirror-attestation-DibE0krH.js → .setup/transcript-mirror-attestation-CzM-94NC.mjs} +4 -1
  112. package/dist/{transport-stdio-BGjHoYLr.js → .setup/transport-stdio-h2VHOskC.mjs} +274 -89
  113. package/dist/{upstream-session-fork-BZerMte_.js → .setup/upstream-session-fork-BUr4OUbC.mjs} +51 -42
  114. package/dist/{usage-tCk-mg_x.js → .setup/usage-BML0r-ua.mjs} +5 -4
  115. package/dist/.setup/usage-limit-error-DUMbefVY.mjs +166 -0
  116. package/dist/{web-search-provider.runtime-B7bGbGWq.js → .setup/web-search-provider.runtime-CDKlN-Rv.mjs} +4 -3
  117. package/dist/api.js +23 -12
  118. package/dist/catalog-page-worker-entrypoint.js +12 -0
  119. package/dist/catalog-page.worker.js +9 -0
  120. package/dist/cli-metadata.js +1 -1
  121. package/dist/doctor-contract-api.js +8 -528
  122. package/dist/harness.js +1 -1
  123. package/dist/index.js +306 -215
  124. package/dist/media-understanding-provider.js +3 -3
  125. package/dist/migration-provider-api.js +1 -1
  126. package/dist/native-tool-policy.js +12 -0
  127. package/dist/provider-discovery.js +18 -0
  128. package/dist/session-history-worker-runtime.js +36 -35
  129. package/dist/session-history.worker.js +2 -2
  130. package/dist/web-search-contract-api.js +1 -1
  131. package/openclaw.plugin.json +354 -69
  132. package/package.json +14 -8
  133. package/dist/attempt-context-D44yeDII.js +0 -636
  134. package/dist/binding-connection-D3udKbIE.js +0 -60
  135. package/dist/event-projector-usage-BfXmpOec.js +0 -102
  136. package/dist/inference-proxy-HGXKUIre.js +0 -311
  137. package/dist/launch-args-RXQwn8zV.js +0 -90
  138. package/dist/native-subagent-history-owner-Pcp7JuVF.js +0 -34
  139. package/dist/native-subagent-monitor-fQ7eIXEf.js +0 -2978
  140. package/dist/node-exec-server.runtime-Cl2vapEi.js +0 -226
  141. package/dist/rpc-error-ttN_QjEm.js +0 -30
  142. package/dist/session-catalog-ct-AH01I.js +0 -1477
  143. package/dist/session-catalog-home-id-CwhLdC03.js +0 -18
  144. package/dist/session-catalog-provenance-D-dIfRAH.js +0 -96
  145. package/dist/timeout-BVw8aaM1.js +0 -33
  146. package/dist/version-omm6IPI3.js +0 -12
  147. /package/dist/{auth-profile-selection-DFu9e167.js → .setup/auth-profile-selection-DFu9e167.mjs} +0 -0
  148. /package/dist/{config-utils-DujwEnhg.js → .setup/config-utils-DujwEnhg.mjs} +0 -0
  149. /package/dist/{context-compaction-activity-CMla5FPJ.js → .setup/context-compaction-activity-CMla5FPJ.mjs} +0 -0
  150. /package/dist/{elicitation-input-DPoK8F7w.js → .setup/elicitation-input-DPoK8F7w.mjs} +0 -0
  151. /package/dist/{history-rejection-B4nOXNXF.js → .setup/history-rejection-B4nOXNXF.mjs} +0 -0
  152. /package/dist/{image-payload-sanitizer-tupAr-2o.js → .setup/image-payload-sanitizer-tupAr-2o.mjs} +0 -0
  153. /package/dist/{incognito-session-uhrBF6wJ.js → .setup/incognito-session-uhrBF6wJ.mjs} +0 -0
  154. /package/dist/{local-runtime-attribution-B_2mnqhb.js → .setup/local-runtime-attribution-B_2mnqhb.mjs} +0 -0
  155. /package/dist/{native-execution-policy-umd0tgh4.js → .setup/native-execution-policy-umd0tgh4.mjs} +0 -0
  156. /package/dist/{rolldown-runtime-8H4AJuhK.js → .setup/rolldown-runtime-8H4AJuhK.mjs} +0 -0
  157. /package/dist/{session-binding-meta-B7aEMU7g.js → .setup/session-binding-meta-B7aEMU7g.mjs} +0 -0
  158. /package/dist/{session-upstream-marker-D15C9NHp.js → .setup/session-upstream-marker-D15C9NHp.mjs} +0 -0
  159. /package/dist/{upstream-prompt-provenance-LphB8slG.js → .setup/upstream-prompt-provenance-LphB8slG.mjs} +0 -0
  160. /package/dist/{web-search-provider.shared-BrZmlqyR.js → .setup/web-search-provider.shared-BrZmlqyR.mjs} +0 -0
@@ -1,72 +1,78 @@
1
- import { s as resolveRecoverableCodexPluginConfigKeys } from "./plugin-inventory-oL4Na1ZG.js";
2
- import { l as sessionBindingIdentity, n as assertCodexBindingMayBeReplaced } from "./session-binding-record-Bcvslnhf.js";
3
- import { n as defineCodexBuildState } from "./build-state-BCmg9-5p.js";
4
- import { b as readCodexRequirementsToml, c as hasCodexMcpToolApprovalOverrides, d as withMcpElicitationsApprovalPolicy, i as resolveCodexAppServerHomeScope, m as resolveCodexModelBackedReviewerPolicyContext, o as resolveCodexComputerUseConfig, u as shouldAutoApproveCodexAppServerApprovals } from "./config-options-CXMq1T-C.js";
5
- import { S as resolveCodexPluginsPolicy, a as flattenCodexDynamicToolFunctions, b as isCodexSandboxExecServerEnabled, o as isJsonObject, s as resolveCodexAppServerHomeDir, v as isCodexPairedNodeRemoteExecPlacementSandbox, x as readCodexPluginConfig, y as isCodexRemoteExecPlacementSandbox } from "./protocol-5bh1G-H7.js";
6
- import { A as isMatchingDynamicToolTerminalDiagnostic, D as handleDynamicToolCallWithTimeout, E as createCodexElicitationResponse, F as toCodexDynamicToolProgressResponse, I as toCodexDynamicToolProtocolResponse, L as resolveCodexToolAbortTerminalReason, M as resolveTerminalDynamicToolBatchAction, N as shouldBlockTerminalReleaseForNonTerminalDynamicToolResult, O as hasPendingDynamicToolTerminalDiagnostic, P as shouldReleaseTurnAfterTerminalDynamicTool, S as readCodexDynamicToolCallParams, T as readCodexTurnCompletedNotification, a as isCodexAppServerBrokenPipeError, c as isCodexAppServerIndeterminateTransportError, d as isCodexAppServerRequestTimeoutError, i as isCodexAppServerApprovalRequest, j as resolveDynamicToolCallTimeoutMs, k as isDynamicToolTerminalDiagnosticEvent, l as isCodexAppServerOverloadError, o as isCodexAppServerConnectionClosedError, p as resolveCodexAppServerClientInstanceId, r as getCodexAppServerClientInstanceId, s as isCodexAppServerIndeterminateRequestCancellationError, w as readCodexTurn, x as assertCodexTurnStartResponse } from "./client-B57KWPQx.js";
7
- import { c as resolveCodexAppServerPreparedApiKeyCacheKey, m as retireSharedCodexAppServerClientIfCurrent, s as resolveCodexAppServerFallbackApiKeyCacheKey } from "./auth-cache-key-7orQae_j.js";
8
- import { t as isIncognitoSessionKey } from "./incognito-session-uhrBF6wJ.js";
9
- import { a as shouldRefreshCodexRateLimitsForUsageLimitMessage, i as resolveCodexUsageLimitResetAtMs, n as formatCodexUsageLimitErrorMessage } from "./rate-limits-CBMmWBr9.js";
10
- import { o as formatCodexDisplayText } from "./command-formatters-lxlkNZgU.js";
11
- import { t as CodexAppServerRpcError } from "./rpc-error-ttN_QjEm.js";
12
- import { t as CODEX_CONTROL_METHODS } from "./capabilities-8vx68WLH.js";
13
- import { D as splitPlanText, E as readNullableString, S as readItemString, T as readNonNegativeInteger, d as matchesCodexSnapshotTurn, f as shouldClearTerminalPresentationForNativeItem, g as extractRawAssistantText, v as readCodexErrorNotificationMessage, x as readItem, y as readCodexProviderRefusal } from "./event-projector-items-5VhsECCO.js";
14
- import { r as readMirrorIdentity, t as attachCodexMirrorIdentity } from "./upstream-prompt-provenance-LphB8slG.js";
15
- import { n as buildCodexAppServerRuntimeFingerprint, r as buildCodexPluginAppCacheKey, s as defaultCodexAppInventoryCache } from "./plugin-app-cache-key-d0eKxy7b.js";
16
- import { t as withAbortableTimeout } from "./timeout-BVw8aaM1.js";
17
- import { n as resolveOpenClawExecPolicyForCodexAppServer, t as canUseCodexModelBackedApprovalsReviewerForModel } from "./config-oIORaQ5T.js";
18
- import { o as hasCodexMirrorOrigin, t as applyCodexTranscriptTaint } from "./transcript-mirror-attestation-DibE0krH.js";
19
- import { r as prepareCodexAppServerAuthBinding } from "./auth-binding-CWlKVccJ.js";
20
- import { i as resolveCodexBindingAppServerConnection, t as assertCodexSessionRuntimeOwnership } from "./binding-connection-D3udKbIE.js";
21
- import { i as resolveCodexAppServerAuthProfileIdForAgent, r as resolveCodexAppServerAuthProfileId } from "./auth-profile-CwzO4_RG.js";
22
- import { A as CodexAppServerStartupError, F as resolveCodexStartupTimeoutMs, G as protectCodexAppServerLiveThread, I as withCodexStartupTimeout, M as TURN_TERMINAL_SETTLEMENT_TIMEOUT_MS, N as isCodexAppServerStartupError, O as getCodexInferenceThread, Q as rememberCodexRateLimitsRead, W as prepareCodexWorkspaceReferences, X as readCodexRateLimitsRevision, Z as readRecentCodexRateLimits, a as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, c as getLeasedSharedCodexAppServerClient, et as resolveCodexAppServerAuthAccountCacheKey, f as readCodexAppServerClientDesktopGenerationFingerprint, h as releaseLeasedSharedCodexAppServerClient, i as clearSharedCodexAppServerClientIfCurrent, j as TURN_FINALIZE_DRAIN_ABORT_GRACE_MS, l as getSharedCodexAppServerClient, s as createIsolatedCodexAppServerClient, tt as resolveCodexAppServerPreparedAuthHandoff, u as isCodexAppServerStartSelectionChangedError, v as retainSharedCodexAppServerClientIfCurrent, z as ensureCodexAppServerClientRuntime } from "./shared-client-CscigXXL.js";
23
- import { a as closeCodexStartupClientBestEffort, d as unsubscribeCodexThreadBestEffort, f as CODEX_APP_SERVER_NATIVE_TURN_WAIT_TIMEOUT_MS, g as readCodexNotificationThreadId, h as isCodexNotificationForTurn, l as retireUnsafeCodexTurnClientBestEffort, m as CodexProjectionDiagnostics, o as interruptCodexTurnAndWaitBestEffort, p as getCodexAppServerTurnRouter, r as CodexAppServerUnsafeSubscriptionError, u as terminateCodexBackgroundTerminals } from "./attempt-client-cleanup-DKHzbwt5.js";
24
- import { a as isRawFunctionToolOutputCompletionNotification, c as readNotificationItemId, i as isPendingOpenClawDynamicToolCompletionNotification, l as readRawResponseToolCallId, o as isTerminalTurnStatus, r as isCodexTurnAbortMarkerNotification, s as readCodexNotificationItem, t as codexExecutionToolName, u as updateActiveTurnItemIds } from "./attempt-notifications-C9XufG6l.js";
25
- import { t as CodexUsageProjection } from "./event-projector-usage-BfXmpOec.js";
26
- import { $ as codexDynamicToolsFingerprint, A as readCodexMcpToolConnectorId, B as createCodexNativeHookRelay, C as resolveCodexWebSearchPlan, Ct as buildCodexPluginThreadConfigInputFingerprint, Et as mergeCodexThreadConfigs, F as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, H as resolveCodexNativeHookRelayEvents, J as loadCodexNativeToolCatalog, L as buildCodexNativeHookRelayConfig, N as isCodexAppServerProfilerEnabled, O as resolveCodexAppServerThreadModelSelection, Ot as shouldBuildCodexPluginThreadConfig, R as buildCodexNativeHookRelayDisabledConfig, St as buildCodexPluginThreadConfig, U as resolveCodexNativeHookRelayTtlMs, V as emitCodexNativePreToolUseFailureDiagnostic, W as scheduleCodexNativeHookRelayUnregister, _t as neutralizeCodexExplicitMentionSigils, a as buildCodexUserInput, bt as resolveCodexContextEngineProjectionReserveTokens, c as startOrResumeThread, d as buildScheduledCodexAppAuthorityInputFingerprint, dt as buildContextEngineBinding, et as codexLegacyDynamicToolsFingerprint, f as buildScheduledCodexAppServerConnectionIdentity, g as resolveScheduledCodexAppCreatorCaptureDecision, gt as isCodexDurableCustomMessage, h as readCurrentCodexScheduledAppPolicy$1, ht as fitCodexProjectedContextForTurnStart, i as buildTurnStartParams, j as readCodexMcpToolUiVisibility, l as assertScheduledCodexAppAuthorityRuntime, m as intersectCodexPluginThreadConfigWithScheduledAuthority, mt as buildCodexContinuityCalibration, p as captureScheduledCodexAppAuthority, pt as CodexContextAttachmentError, q as hasCodexNativeToolCatalog, r as buildTurnCollaborationMode, rt as filterCodexDynamicTools, st as isSystemAgentOnlyCodexDynamicToolAllowlist, t as buildCodexParentLocalInstructions, u as buildLegacyScheduledCodexAppRecoveryPrompt, ut as resolveCodexDynamicToolsLoadingForRuntime, vt as projectContextEngineAssemblyForCodex, w as buildDeveloperInstructions, wt as buildCodexPluginThreadConfigTimeoutFallback, xt as resolveCodexContinuityProjectionMaxChars, yt as resolveCodexContextEngineProjectionMaxChars } from "./thread-lifecycle-DXl1-I6b.js";
27
- import { a as resolveCodexRunSessionBindingAuthority, n as createCodexSessionGenerationSupersededError, o as resolveCodexSessionBinding, s as scopeCodexRunBindingStore } from "./session-binding-CI8UuilT.js";
28
- import { n as CodexThreadPolicyHandoffError } from "./thread-policy-BuEQfE0g.js";
29
- import { r as retainCodexAppServerBindingSubscription } from "./thread-ownership-BsDRTsyp.js";
30
- import { i as mirrorPromptAtTurnStartBestEffort, n as createCodexAppServerUserMessagePersistenceNotifier, o as buildCodexUserPromptMessage, s as promptSnapshot, t as codexTranscriptMirrorRuntime } from "./transcript-mirror-C5CKgNy6.js";
31
- import { t as persistCodexContextCompactionActivity } from "./context-compaction-activity-CMla5FPJ.js";
32
- import { t as CODEX_INFERENCE_GENERATION_KEY } from "./inference-context-Dr_mzvk4.js";
33
- import { i as createAssistantReasoningMessage, n as createAssistantCommentaryMessage, r as createAssistantMessage, t as createAssistantAsyncMessage } from "./event-projector-assistant-message-BmBM4qBV.js";
34
- import { _ as sanitizeCodexToolResponse, g as sanitizeCodexToolArguments, h as resolveCodexToolProgressDetailMode, m as isCodexCommandBearingToolCall, p as inferCodexDynamicToolMeta } from "./native-subagent-task-ids-BzTdT0Uw.js";
35
- import { n as createCodexNativeSubagentHistoryOwner } from "./native-subagent-history-owner-Pcp7JuVF.js";
36
- import { C as shouldRequireCodexSandboxExecServerEnvironment, S as shouldEnableCodexAppServerNativeToolSurface, _ as resolveCodexAppServerHookChannelId, a as emitDynamicToolStartedDiagnostic, c as ensureCodexSandboxExecServerEnvironment, d as CodexNativeToolLifecycleProjector, f as buildDynamicTools, g as resolveCodexAppServerExecutionCwd, h as formatCodexDynamicToolBuildStageSummary, i as emitDynamicToolErrorDiagnostic, l as releaseCodexSandboxExecServerEnvironment, m as disableCodexPluginThreadConfig, n as projectCodexExecutableDynamicTools, o as emitDynamicToolTerminalDiagnostic, p as createCodexDynamicToolBuildStageTracker, r as routeCodexAppServerElicitationRequest, s as handleCodexAppServerApprovalRequest, t as createCodexDynamicToolBridge, u as readBoundedCodexRemoteWorkspaceFile, v as resolveCodexExternalSandboxPolicyForOpenClawSandbox, w as shouldWarnCodexDynamicToolBuildStageSummary, x as resolveCodexSandboxEnvironmentSelection, y as resolveCodexMessageToolProvider } from "./dynamic-tools-CmN5RAip.js";
37
- import { t as resolveCodexProviderWebSearchSupport } from "./provider-capabilities-BvCUL69A.js";
38
- import { a as requestPluginApprovalOutcome } from "./plugin-approval-roundtrip-Bz3FT8W5.js";
39
- import { a as CodexToolProgressProjection, i as CodexToolTranscriptProjection, n as CodexEventProjection, o as shouldEmitTranscriptToolProgress, r as emitCodexAgentEvent, t as codexNativeSubagentMonitorRuntime } from "./native-subagent-monitor-fQ7eIXEf.js";
40
- import { a as resolveCodexAppServerForModelProvider, i as resolveCodexSessionPermissionCwd, n as applyCodexSessionPermissionPolicy, r as resolveCodexEffectiveSessionPermissionPolicy } from "./session-permission-policy-BBmXA8Eg.js";
41
- import { a as getCodexWorkspaceMemoryToolNames, c as readContextEngineThreadBootstrapProjection, d as resolveCodexDeliveryHintPreservedInputRange, f as resolveContextEngineBootstrapProjectionDecision, i as buildCodexWorkspaceBootstrapContext, l as readMirroredSessionHistoryMessages, n as buildCodexSystemPromptReport, r as buildCodexWatchedSessionsContext, s as prependCodexOpenClawPromptContext, t as buildCodexOpenClawPromptContext, u as renderCodexSkillsCollaborationInstructions } from "./attempt-context-D44yeDII.js";
42
- import { n as captureCodexSettledTurnFinalizationContext } from "./settled-turn-context-Du6gdUDd.js";
43
- import { a as runCodexComputerUseLiveTest, n as ensureCodexComputerUse, t as defaultCodexPluginMetadataCache } from "./plugin-metadata-cache-ClPJpRio.js";
44
- import { asOptionalRecord, normalizeOptionalString, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
1
+ import { s as resolveRecoverableCodexPluginConfigKeys } from "./plugin-inventory-BoRei8Z4.mjs";
2
+ import { i as matchesCodexNativeSubagentSubmissionBinding, m as sessionBindingIdentity, n as assertCodexBindingMayBeReplaced, x as createCodexNativeSubagentHistoryOwner } from "./session-binding-record-BGoz8wOK.mjs";
3
+ import { n as defineCodexBuildState } from "./build-state-C7EnDVgr.mjs";
4
+ import { d as resolveCodexAppServerHomeDir, l as shouldAutoApproveCodexAppServerApprovals, n as hasCodexMcpToolApprovalOverrides, u as withMcpElicitationsApprovalPolicy, y as readCodexRequirementsToml } from "./config-security-BEReZ6go.mjs";
5
+ import { c as isCodexPairedNodeRemoteExecPlacementSandbox, d as readCodexPluginConfig, f as resolveCodexPluginsPolicy, l as isCodexRemoteExecPlacementSandbox, u as isCodexSandboxExecServerEnabled } from "./config-parsing-CcB9iPoq.mjs";
6
+ import { i as isJsonObject, r as flattenCodexDynamicToolFunctions } from "./protocol-CANUwXJ3.mjs";
7
+ import { i as resolveCodexAppServerHomeScope, l as resolveCodexModelBackedReviewerPolicyContext, o as resolveCodexComputerUseConfig } from "./config-options-BvaRs51b.mjs";
8
+ import { c as resolveCodexAppServerPreparedApiKeyCacheKey, s as resolveCodexAppServerFallbackApiKeyCacheKey } from "./auth-cache-key-B2mpaI9D.mjs";
9
+ import { h as retireSharedCodexAppServerClientIfCurrent } from "./session-catalog-events-Bj6j94E_.mjs";
10
+ import { t as isIncognitoSessionKey } from "./incognito-session-uhrBF6wJ.mjs";
11
+ import { o as formatCodexDisplayText } from "./command-formatters-Bmqvr9tO.mjs";
12
+ import { n as waitForPromiseOrAbort, o as CodexAppServerRpcError, r as withAbortableTimeout } from "./timeout-C910MdAB.mjs";
13
+ import { t as readCodexAsyncQuestions } from "./async-questions-DdhOCwGi.mjs";
14
+ import { C as readItemString, D as readNullableString, E as readNonNegativeInteger, O as splitPlanText, S as readItem, _ as extractRawAssistantText, b as readCodexProviderRefusal, d as matchesCodexSnapshotTurn, f as shouldClearTerminalPresentationForNativeItem, y as readCodexErrorNotificationMessage } from "./event-projector-items-C6L_5ZiT.mjs";
15
+ import { r as readMirrorIdentity, t as attachCodexMirrorIdentity } from "./upstream-prompt-provenance-LphB8slG.mjs";
16
+ import { a as defaultCodexAppInventoryCache, n as buildCodexAppServerRuntimeFingerprint, r as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-B2CsSdnV.mjs";
17
+ import { i as resolveCodexBindingAppServerConnection, t as assertCodexSessionRuntimeOwnership } from "./binding-connection-ThoatDcb.mjs";
18
+ import { n as resolveOpenClawExecPolicyForCodexAppServer, t as canUseCodexModelBackedApprovalsReviewerForModel } from "./config-BoTP_mrL.mjs";
19
+ import { o as hasCodexMirrorOrigin, t as applyCodexTranscriptTaint } from "./transcript-mirror-attestation-CzM-94NC.mjs";
20
+ import { t as readCodexMirroredSessionHistoryMessages } from "./session-history-BrqhSCf9.mjs";
21
+ import { i as resolveCodexAppServerAuthProfileIdForAgent, r as resolveCodexAppServerAuthProfileId } from "./auth-profile-WqZtZfXN.mjs";
22
+ import { A as getCodexInferenceThread, B as prepareCodexWorkspaceReferences, K as readCodexRateLimitsRevision, P as ensureCodexAppServerClientRuntime, V as protectCodexAppServerLiveThread, X as resolveCodexAppServerAuthAccountCacheKey, Z as resolveCodexAppServerPreparedAuthHandoff, a as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, at as resolveCodexStartupTimeoutMs, c as getLeasedSharedCodexAppServerClient, et as CodexAppServerStartupError, f as readCodexAppServerClientDesktopGenerationFingerprint, h as releaseLeasedSharedCodexAppServerClient, i as clearSharedCodexAppServerClientIfCurrent, l as getSharedCodexAppServerClient, nt as TURN_TERMINAL_SETTLEMENT_TIMEOUT_MS, ot as withCodexStartupTimeout, q as readRecentCodexRateLimits, s as createIsolatedCodexAppServerClient, tt as TURN_FINALIZE_DRAIN_ABORT_GRACE_MS, u as isCodexAppServerStartSelectionChangedError, v as retainSharedCodexAppServerClientIfCurrent, w as CodexThreadClientReplacementError } from "./shared-client-DA4VR4Eb.mjs";
23
+ import { A as isMatchingDynamicToolTerminalDiagnostic, B as CODEX_APP_SERVER_OPT_OUT_NOTIFICATION_METHODS, C as readCodexDynamicToolCallParams, D as handleDynamicToolCallWithTimeout, E as createCodexElicitationResponse, F as toCodexDynamicToolProgressResponse, I as toCodexDynamicToolProtocolResponse, L as resolveCodexToolAbortTerminalReason, M as resolveTerminalDynamicToolBatchAction, N as shouldBlockTerminalReleaseForNonTerminalDynamicToolResult, O as hasPendingDynamicToolTerminalDiagnostic, P as shouldReleaseTurnAfterTerminalDynamicTool, S as assertCodexTurnStartResponse, T as readCodexTurnCompletedNotification, c as isCodexAppServerIndeterminateTransportError, i as isCodexAppServerApprovalRequest, j as resolveDynamicToolCallTimeoutMs, k as isDynamicToolTerminalDiagnosticEvent, m as CodexServerRequestResolvedError, o as isCodexAppServerConnectionClosedError, p as resolveCodexAppServerClientInstanceId, r as getCodexAppServerClientInstanceId, s as isCodexAppServerIndeterminateRequestCancellationError } from "./client-Cs08OXVQ.mjs";
24
+ import { _ as CodexProjectionDiagnostics, d as shouldRetireCodexStartupClient, f as terminateCodexBackgroundTerminals, h as getCodexAppServerTurnRouter, m as CODEX_APP_SERVER_NATIVE_TURN_WAIT_TIMEOUT_MS, n as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, o as closeCodexStartupClientBestEffort, p as unsubscribeCodexThreadBestEffort, r as CodexAppServerUnsafeSubscriptionError, s as interruptCodexTurnAndWaitBestEffort, u as retireUnsafeCodexTurnClientBestEffort, v as isCodexNotificationForTurn, y as readCodexNotificationThreadId } from "./attempt-client-cleanup-CEv1cKwF.mjs";
25
+ import { r as prepareCodexAppServerAuthBinding } from "./auth-binding-DhLWa_7K.mjs";
26
+ import { a as isPendingOpenClawDynamicToolCompletionNotification, c as readCodexNotificationItem, d as updateActiveTurnItemIds, i as isCodexTurnAbortMarkerNotification, l as readNotificationItemId, o as isRawFunctionToolOutputCompletionNotification, r as codexExecutionToolName, s as isTerminalTurnStatus, t as CodexUsageProjection, u as readRawResponseToolCallId } from "./event-projector-usage-CnBS8Wa3.mjs";
27
+ import { A as emitCodexNativePreToolUseFailureDiagnostic, C as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, Ct as shouldBuildCodexPluginThreadConfig, D as buildCodexNativeHookRelayDisabledConfig, E as buildCodexNativeHookRelayConfig, K as codexDynamicToolsFingerprint, M as resolveCodexNativeHookRelayTtlMs, N as scheduleCodexNativeHookRelayUnregister, R as hasCodexNativeToolCatalog, T as assertCodexNativeHookRelayAllowed, U as areCodexDynamicToolFingerprintsCompatible, X as fingerprintJsonObject, at as filterCodexDynamicTools, ft as resolveCodexDynamicToolsLoadingForRuntime, gt as buildCodexPluginThreadConfigTimeoutFallback, ht as buildCodexPluginThreadConfigInputFingerprint, j as resolveCodexNativeHookRelayEvents, k as createCodexNativeHookRelay, lt as isSystemAgentOnlyCodexDynamicToolAllowlist, m as buildDeveloperInstructions, mt as buildCodexPluginThreadConfig, p as resolveCodexWebSearchPlan, q as codexLegacyDynamicToolsFingerprint, v as resolveCodexAppServerThreadModelSelection, w as CodexManagedHooksOnlyError, xt as mergeCodexThreadConfigs, z as loadCodexNativeToolCatalog } from "./thread-requests-BLvGkP2R.mjs";
28
+ import { A as resolveCodexContinuityProjectionMaxChars, C as buildCodexContinuityCalibration, D as projectContextEngineAssemblyForCodex, E as neutralizeCodexExplicitMentionSigils, O as resolveCodexContextEngineProjectionMaxChars, S as CodexContextAttachmentError, T as isCodexDurableCustomMessage, _ as readCodexMcpToolUiVisibility, a as buildTurnStartParams, b as isContextEngineBindingCompatible, c as assertScheduledCodexAppAuthorityRuntime, d as buildScheduledCodexAppServerConnectionIdentity, f as captureScheduledCodexAppAuthority, g as readCodexMcpToolConnectorId, h as resolveScheduledCodexAppCreatorCaptureDecision, i as buildTurnCollaborationMode, k as resolveCodexContextEngineProjectionReserveTokens, l as buildLegacyScheduledCodexAppRecoveryPrompt, m as readCurrentCodexScheduledAppPolicy$1, n as buildCodexParentLocalInstructions, o as buildCodexUserInput, p as intersectCodexPluginThreadConfigWithScheduledAuthority, s as startOrResumeThread, t as buildCodexHistoryProvenancePrefix, u as buildScheduledCodexAppAuthorityInputFingerprint, v as isCodexAppServerProfilerEnabled, w as fitCodexProjectedContextForTurnStart, x as CODEX_TURN_START_TEXT_INPUT_MAX_CHARS, y as buildContextEngineBinding } from "./thread-lifecycle-B3mfz1TG.mjs";
29
+ import { a as resolveCodexRunSessionBindingAuthority, n as createCodexSessionGenerationSupersededError, o as resolveCodexSessionBinding } from "./session-binding-Cm0apEbd.mjs";
30
+ import { n as CodexThreadPolicyHandoffError } from "./thread-policy-DGuQsuFC.mjs";
31
+ import { r as retainCodexAppServerBindingSubscription, s as withExclusiveCodexAppServerThread, t as isSameCodexAppServerThreadOwner } from "./thread-ownership-DcTtAcXj.mjs";
32
+ import { c as promptSnapshot, i as mirrorPromptAtTurnStartBestEffort, n as createCodexAppServerUserMessagePersistenceNotifier, o as buildCodexUserPromptMessage, s as buildResolvedCodexUserPromptMessage, t as codexTranscriptMirrorRuntime } from "./transcript-mirror-BtHDBp1G.mjs";
33
+ import { a as resolveCodexPromptError, i as refreshCodexUsageLimitPromptError, n as formatCodexTurnStartUsageLimitError, r as markCodexAuthProfileBlockedFromRateLimits, t as CodexUsageLimitPromptError } from "./usage-limit-error-DUMbefVY.mjs";
34
+ import { t as persistCodexContextCompactionActivity } from "./context-compaction-activity-CMla5FPJ.mjs";
35
+ import { t as attemptTerminal } from "./attempt-terminal-gZrxQ35N.mjs";
36
+ import { a as shouldKeepCodexSharedAbortOpen, i as runCodexAgentEndHook, n as ensureCodexWorkspaceDirOnce, o as withCodexAppServerFastModeServiceTier, r as estimateCodexAppServerProjectedTurnTokens, t as emitCodexAppServerEvent } from "./run-attempt-lifecycle-B7JbY4-F.mjs";
37
+ import { t as CODEX_INFERENCE_GENERATION_KEY } from "./inference-context-D96l9-ew.mjs";
38
+ import { i as createAssistantReasoningMessage, n as createAssistantCommentaryMessage, r as createAssistantMessage, t as createAssistantAsyncMessage } from "./event-projector-assistant-message-BsruvNrH.mjs";
39
+ import { C as sanitizeCodexToolArguments, S as resolveCodexToolProgressDetailMode, b as inferCodexDynamicToolMeta, w as sanitizeCodexToolResponse, x as isCodexCommandBearingToolCall } from "./native-subagent-task-ids-wb8CKZGN.mjs";
40
+ import { C as shouldEnableCodexAppServerNativeToolSurface, E as mapCodexAppServerRemoteWorkspacePath, S as resolveCodexSandboxEnvironmentSelection, T as shouldWarnCodexDynamicToolBuildStageSummary, _ as formatCodexDynamicToolBuildStageSummary, a as emitDynamicToolStartedDiagnostic, b as resolveCodexExternalSandboxPolicyForOpenClawSandbox, c as ensureCodexSandboxExecServerEnvironment, d as hasCodexNativeBackgroundProcesses, f as readBoundedCodexRemoteWorkspaceFile, g as disableCodexPluginThreadConfig, h as createCodexDynamicToolBuildStageTracker, i as emitDynamicToolErrorDiagnostic, l as releaseCodexSandboxExecServerEnvironment, m as buildDynamicTools, n as projectCodexExecutableDynamicTools, o as emitDynamicToolTerminalDiagnostic, p as CodexNativeToolLifecycleProjector, r as routeCodexAppServerElicitationRequest, s as handleCodexAppServerApprovalRequest, t as createCodexDynamicToolBridge, u as CodexNativeProcessAuthority, v as resolveCodexAppServerExecutionCwd, w as shouldRequireCodexSandboxExecServerEnvironment, x as resolveCodexMessageToolProvider, y as resolveCodexAppServerHookChannelId } from "./dynamic-tools-BDXl-8Cw.mjs";
41
+ import { t as resolveCodexProviderWebSearchSupport } from "./provider-capabilities-BfE88FZ5.mjs";
42
+ import { a as requestPluginApprovalOutcome } from "./plugin-approval-roundtrip-C0MZzCuP.mjs";
43
+ import { a as CodexToolProgressProjection, i as CodexToolTranscriptProjection, n as CodexEventProjection, o as shouldEmitTranscriptToolProgress, r as emitCodexAgentEvent, t as codexNativeSubagentMonitorRuntime } from "./native-subagent-monitor-D3filLY2.mjs";
44
+ import { a as resolveCodexAppServerForModelProvider, i as resolveCodexSessionPermissionCwd, n as applyCodexSessionPermissionPolicy, r as resolveCodexEffectiveSessionPermissionPolicy } from "./session-permission-policy-BI1QK_98.mjs";
45
+ import { a as getCodexContextFileDisplayBasename, c as normalizeCodexContextFilePath, d as shouldInjectCodexOpenClawPromptContext, i as getCodexContextFileBasename, l as normalizeCodexDynamicToolName, n as CODEX_NATIVE_PROJECT_DOC_BASENAMES, o as getCodexWorkspaceMemoryToolNames, r as buildCodexWorkspaceBootstrapContext, s as isNonEmptyString$1 } from "./attempt-workspace-context-DUVEn_CS.mjs";
46
+ import { n as captureCodexSettledTurnFinalizationContext } from "./settled-turn-context-fmwouT5I.mjs";
47
+ import { a as runCodexComputerUseLiveTest, n as ensureCodexComputerUse, t as defaultCodexPluginMetadataCache } from "./plugin-metadata-cache-Ctmo83k_.mjs";
48
+ import { asOptionalRecord, normalizeOptionalString, readNonBlankString, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
45
49
  import { createHash } from "node:crypto";
50
+ import { isDeepStrictEqual } from "node:util";
46
51
  import { resolveAgentConfig, resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
47
52
  import { MAX_TIMER_TIMEOUT_MS, asDateTimestampMs, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
48
53
  import path from "node:path";
49
54
  import fs from "node:fs/promises";
55
+ import { isPathStrictlyInside, readFileWindowFully, root } from "openclaw/plugin-sdk/file-access-runtime";
50
56
  import { addAbortListener } from "node:events";
51
57
  import { truncateUtf16Safe, truncateUtf8Prefix } from "openclaw/plugin-sdk/text-utility-runtime";
52
- import { Buffer as Buffer$1 } from "node:buffer";
58
+ import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent, emitTrustedDiagnosticEventWithPrivateData, freezeDiagnosticTraceContext, onInternalDiagnosticEvent, resolveDiagnosticModelContentCapturePolicy } from "openclaw/plugin-sdk/diagnostic-runtime";
53
59
  import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
54
- import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
55
- import { markAuthProfileBlockedUntil, resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
56
- import { AgentHarnessPreflightError, CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, FAST_MODE_AUTO_PROGRESS_KIND, agentHarnessAttemptTerminal, agentHarnessStructuredInput, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentEndSideEffects, bootstrapHarnessContextEngine, buildAgentHookContextChannelFields, buildEmbeddedForegroundPromptContext, buildHarnessContextEngineRuntimeContext, cancelPendingAgentQuestionForSession, claimPendingAgentQuestionAnswer, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, detectAndLoadAgentHarnessPromptImages, embeddedAgentLog, emitAgentEvent, emptyAgentHarnessUserInputAnswers, formatErrorMessage, formatFastModeAutoProgressText, getAgentHarnessHookRunner, hasBeforeToolCallPolicy, isActiveHarnessContextEngine, isHostScopedAgentToolActive, loadCodexBundleMcpThreadConfig, materializeRequesterScopedMcpToolsForHarnessRun, prepareHarnessNativeMcpAppPreview, projectAgentHarnessTranscriptMessageForDisplay, projectProgressCardChannelUpdate, resolveAgentDir as resolveAgentDir$2, resolveAgentHarnessBeforePromptBuildResult, resolveAgentRunAbortLifecycleFields, resolveAttemptFsWorkspaceOnly, resolveContextEngineOwnerPluginId, resolveFastModeForElapsed, resolveSandboxContext, resolveUserPath, runAgentCleanupStep, runAgentEndSideEffects, runAgentHarnessAfterCompactionHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
60
+ import { Buffer as Buffer$1 } from "node:buffer";
61
+ import { resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
62
+ import { AgentHarnessPreflightError, CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, FAST_MODE_AUTO_PROGRESS_KIND, agentHarnessStructuredInput, assembleHarnessContextEngine, assertContextEngineHostSupport, bootstrapHarnessContextEngine, buildAgentHookContextChannelFields, buildEmbeddedForegroundPromptContext, buildHarnessContextEngineRuntimeContext, buildWatchedSessionsHarnessContext, cancelPendingAgentQuestionForSession, claimPendingAgentQuestionAnswer, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, detectAndLoadAgentHarnessPromptImages, embeddedAgentLog, emptyAgentHarnessUserInputAnswers, formatErrorMessage, formatFastModeAutoProgressText, getAgentHarnessHookRunner, hasBeforeToolCallPolicy, isActiveHarnessContextEngine, isHostScopedAgentToolActive, loadCodexBundleMcpThreadConfig, materializeRequesterScopedMcpToolsForHarnessRun, prepareHarnessNativeMcpAppPreview, projectAgentHarnessTranscriptMessageForDisplay, projectAgentToolActivity, projectProgressCardChannelUpdate, resolveAgentDir as resolveAgentDir$2, resolveAgentHarnessBeforePromptBuildResult, resolveAgentRunAbortLifecycleFields, resolveAttemptFsWorkspaceOnly, resolveContextEngineOwnerPluginId, resolveFastModeForElapsed, resolveSandboxContext, resolveUserPath, runAgentCleanupStep, runAgentHarnessAfterCompactionHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
57
63
  import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
58
- import { isDeepStrictEqual } from "node:util";
59
- import { getSessionEntry, parseSqliteSessionFileMarker } from "openclaw/plugin-sdk/session-store-runtime";
60
- import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent, emitTrustedDiagnosticEventWithPrivateData, freezeDiagnosticTraceContext, onInternalDiagnosticEvent, resolveDiagnosticModelContentCapturePolicy } from "openclaw/plugin-sdk/diagnostic-runtime";
64
+ import { getSessionEntry } from "openclaw/plugin-sdk/session-store-runtime";
65
+ import { acknowledgeInternalToolResult } from "openclaw/plugin-sdk/agent-harness-tool-runtime";
61
66
  import { captureFinalCodexCronCreatorToolAllowlist, formatMcpCodexApprovalRemedy, materializeStaticMcpToolsForHarnessRun, resolveCodexMcpToolOverridesForAgent, resolveCodexTtsProvenanceTransfer } from "openclaw/plugin-sdk/codex-mcp-projection";
62
67
  import { appendSessionYieldContext, hasPromptImageInput } from "openclaw/plugin-sdk/session-transcript-runtime";
68
+ import { prepareAgentWorkspaceAttachments } from "openclaw/plugin-sdk/agent-workspace-runtime";
63
69
  import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-local-roots";
64
70
  import { isSilentReplyPayloadText } from "openclaw/plugin-sdk/reply-chunking";
65
71
  import { generatedImageAssetFromBase64 } from "openclaw/plugin-sdk/image-generation";
66
72
  import { resolveGeneratedMediaMaxBytes } from "openclaw/plugin-sdk/media-generation-runtime";
67
- import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
73
+ import { normalizeMediaReferenceForComparison, saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
68
74
  import { stripInvisibleUnicode } from "openclaw/plugin-sdk/web-content-extractor";
69
- import { acknowledgeInternalToolResult } from "openclaw/plugin-sdk/agent-harness-tool-runtime";
75
+ import { MESSAGE_TOOL_DELIVERY_HINTS } from "openclaw/plugin-sdk/message-tool-delivery-hints";
70
76
  import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
71
77
  //#region extensions/codex/src/app-server/attempt-preparation-timing.ts
72
78
  /** Records startup before the native turn owns inference and tool execution. */
@@ -126,6 +132,7 @@ var CodexSteeringAcceptedUnconfirmedError = class extends Error {
126
132
  * app-server `turn/steer` requests.
127
133
  */
128
134
  function createCodexSteeringQueue(params) {
135
+ const acceptedMessages = [];
129
136
  let batchedMessages = [];
130
137
  const dispatchedBatches = /* @__PURE__ */ new Map();
131
138
  const pendingMessages = /* @__PURE__ */ new Set();
@@ -151,9 +158,13 @@ function createCodexSteeringQueue(params) {
151
158
  item.acceptance = accepted ? "accepted" : "rejected";
152
159
  item.onQueueAccepted?.(accepted);
153
160
  };
161
+ const acceptItem = (item) => {
162
+ if (item.acceptance === "open") acceptedMessages.push(item.prepared.message);
163
+ reportItemAcceptance(item, true);
164
+ };
154
165
  const resolveItem = (item) => {
155
166
  if (item.settled) return;
156
- reportItemAcceptance(item, true);
167
+ acceptItem(item);
157
168
  item.settled = true;
158
169
  pendingMessages.delete(item);
159
170
  item.resolve();
@@ -171,7 +182,7 @@ function createCodexSteeringQueue(params) {
171
182
  params.signal.removeEventListener("abort", abortQueue);
172
183
  clearBatchTimer();
173
184
  batchedMessages = [];
174
- for (const batch of dispatchedBatches.values()) for (const item of batch.items) reportItemAcceptance(item, true);
185
+ for (const batch of dispatchedBatches.values()) for (const item of batch.items) acceptItem(item);
175
186
  dispatchedBatches.clear();
176
187
  for (const item of pendingMessages) rejectItem(item, error);
177
188
  };
@@ -190,13 +201,14 @@ function createCodexSteeringQueue(params) {
190
201
  closeQueue(/* @__PURE__ */ new Error("codex app-server steering queue cancelled"));
191
202
  };
192
203
  const sendBatch = async (items) => {
193
- let liveItems = items.filter((item) => !item.settled);
194
- if (liveItems.length === 0) return;
204
+ const pendingItems = items.filter((item) => !item.settled);
205
+ let liveItems = [];
206
+ if (pendingItems.length === 0) return;
195
207
  let clientUserMessageId;
196
208
  let skippedRevokedBatch = false;
197
209
  try {
198
210
  assertActive();
199
- const prepared = /* @__PURE__ */ new Map();
211
+ const prepared = [];
200
212
  const isCurrent = (item) => {
201
213
  if (item.settled) return false;
202
214
  try {
@@ -207,17 +219,20 @@ function createCodexSteeringQueue(params) {
207
219
  return false;
208
220
  }
209
221
  };
210
- for (const item of liveItems) {
222
+ for (const item of pendingItems) {
211
223
  if (!isCurrent(item)) continue;
212
224
  try {
213
- prepared.set(item, await params.prepareMessage(item.text, item));
225
+ prepared.push(Object.assign(item, { prepared: await params.prepareMessage(item.text, item, () => {
226
+ assertActive();
227
+ item.assertCurrent();
228
+ }) }));
214
229
  } catch (error) {
215
230
  if (isCurrent(item)) throw error;
216
231
  }
217
232
  assertActive();
218
233
  isCurrent(item);
219
234
  }
220
- liveItems = liveItems.filter(isCurrent);
235
+ liveItems = prepared.filter(isCurrent);
221
236
  if (liveItems.length === 0) return;
222
237
  if (params.beforeSubmit) {
223
238
  await params.beforeSubmit(liveItems);
@@ -230,7 +245,7 @@ function createCodexSteeringQueue(params) {
230
245
  const request = {
231
246
  threadId: params.threadId,
232
247
  expectedTurnId: params.turnId,
233
- input: liveItems.flatMap((item) => prepared.get(item) ?? []),
248
+ input: liveItems.flatMap((item) => item.prepared.input),
234
249
  clientUserMessageId
235
250
  };
236
251
  await params.client.request("turn/steer", request, {
@@ -239,7 +254,7 @@ function createCodexSteeringQueue(params) {
239
254
  assertCurrent: () => {
240
255
  assertActive();
241
256
  liveItems = liveItems.filter(isCurrent);
242
- request.input = liveItems.flatMap((item) => prepared.get(item) ?? []);
257
+ request.input = liveItems.flatMap((item) => item.prepared.input);
243
258
  dispatchedBatches.set(request.clientUserMessageId, { items: liveItems });
244
259
  if (liveItems.length === 0) {
245
260
  skippedRevokedBatch = true;
@@ -247,15 +262,12 @@ function createCodexSteeringQueue(params) {
247
262
  }
248
263
  }
249
264
  });
250
- for (const item of liveItems) reportItemAcceptance(item, true);
265
+ for (const item of liveItems) acceptItem(item);
251
266
  } catch (error) {
252
267
  if (clientUserMessageId) dispatchedBatches.delete(clientUserMessageId);
253
268
  if (skippedRevokedBatch) return;
254
- const acceptedUnconfirmed = clientUserMessageId !== void 0 && (isCodexAppServerIndeterminateRequestCancellationError(error) || isCodexAppServerIndeterminateTransportError(error));
255
- for (const item of liveItems) {
256
- if (acceptedUnconfirmed) reportItemAcceptance(item, true);
257
- rejectItem(item, error);
258
- }
269
+ if (clientUserMessageId !== void 0 && (isCodexAppServerIndeterminateRequestCancellationError(error) || isCodexAppServerIndeterminateTransportError(error))) for (const item of liveItems) acceptItem(item);
270
+ for (const item of items) rejectItem(item, error);
259
271
  throw error;
260
272
  }
261
273
  };
@@ -328,6 +340,7 @@ function createCodexSteeringQueue(params) {
328
340
  for (const item of batch.items) resolveItem(item);
329
341
  return true;
330
342
  },
343
+ getAcceptedMessages: () => acceptedMessages.slice(),
331
344
  sealAdmission: sealQueueAdmission,
332
345
  cancel: cancelQueue
333
346
  };
@@ -337,9 +350,6 @@ function normalizeCodexSteerDebounceMs(value) {
337
350
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : CODEX_STEER_ALL_DEBOUNCE_MS;
338
351
  }
339
352
  //#endregion
340
- //#region extensions/codex/src/app-server/attempt-terminal.ts
341
- const attemptTerminal = agentHarnessAttemptTerminal;
342
- //#endregion
343
353
  //#region extensions/codex/src/app-server/event-projector-assistant.ts
344
354
  var CodexAssistantProjection = class {
345
355
  constructor(params, emitAgentEvent, matchesToolProgressEcho, nextTranscriptTimestamp, checkpointCommentary) {
@@ -353,6 +363,7 @@ var CodexAssistantProjection = class {
353
363
  this.assistantTimestampByItem = /* @__PURE__ */ new Map();
354
364
  this.assistantPhaseByItem = /* @__PURE__ */ new Map();
355
365
  this.assistantDeliveryByItem = /* @__PURE__ */ new Map();
366
+ this.assistantQuestionsByItem = /* @__PURE__ */ new Map();
356
367
  this.latestTerminalAssistantCandidateSuperseded = false;
357
368
  this.terminalAssistantCandidateEarlierActiveItemIds = /* @__PURE__ */ new Set();
358
369
  this.lastCommentaryProgressEventByItem = /* @__PURE__ */ new Map();
@@ -416,7 +427,7 @@ var CodexAssistantProjection = class {
416
427
  });
417
428
  if (knownFinalAnswer && !replaceable) await this.params.onPartialReply?.(streamPayload);
418
429
  }
419
- recordItemStarted(item, itemId) {
430
+ async recordItemStarted(item, itemId) {
420
431
  this.noteNativeWorkBarrier(item);
421
432
  this.rememberAssistantPhase(item);
422
433
  if (item?.type === "agentMessage" && itemId && !this.isNonTerminalAssistantItem(itemId) && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
@@ -425,6 +436,10 @@ var CodexAssistantProjection = class {
425
436
  this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
426
437
  if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
427
438
  }
439
+ if (item?.type === "agentMessage" && itemId && item.text && !this.assistantTextByItem.has(itemId)) await this.handleAssistantDelta({
440
+ itemId,
441
+ delta: item.text
442
+ });
428
443
  }
429
444
  recordItemCompleted(item, itemId, activeItemIds) {
430
445
  if (itemId && item?.type === "agentMessage") this.completedAssistantItemIds.add(itemId);
@@ -441,7 +456,7 @@ var CodexAssistantProjection = class {
441
456
  if (item?.type === "agentMessage" && typeof item.text === "string") {
442
457
  this.rememberAssistantItem(item.id);
443
458
  this.assistantTextByItem.set(item.id, item.text);
444
- if (item.text && this.isCommentaryAssistantItem(item.id)) {
459
+ if (this.isCommentaryAssistantItem(item.id)) {
445
460
  this.emitCommentaryProgress({
446
461
  itemId: item.id,
447
462
  text: item.text,
@@ -475,15 +490,11 @@ var CodexAssistantProjection = class {
475
490
  this.pendingRawCommentaryEchoes -= 1;
476
491
  return;
477
492
  }
478
- const text = extractRawAssistantText(item);
479
493
  if (isPendingTerminalAssistantEcho) {
480
- const typedItemId = pendingTerminalAssistantEchoItemId;
481
494
  this.pendingRawTerminalAssistantEchoItemId = void 0;
482
- if (this.assistantTextByItem.get(typedItemId)?.trim() || !text) return;
483
- this.rememberAssistantItem(typedItemId);
484
- this.assistantTextByItem.set(typedItemId, text);
485
495
  return;
486
496
  }
497
+ const text = extractRawAssistantText(item);
487
498
  if (text === void 0 || !text && (phase === "commentary" || activeItemIds.size > 0 || readStringField(item, "type") !== "message")) return;
488
499
  const itemId = rawItemId ?? `raw-assistant-${this.assistantItemOrder.length + 1}`;
489
500
  const isIdlessTerminalAssistantAfterCompletedWork = candidateWasSupersededBeforeRaw && rawItemId === void 0 && pendingTerminalAssistantEchoItemId === void 0 && activeItemIds.size === 0;
@@ -528,14 +539,11 @@ var CodexAssistantProjection = class {
528
539
  }
529
540
  collectAsyncMessages() {
530
541
  return this.assistantItemOrder.flatMap((itemId) => {
531
- if (!this.isAsyncAssistantItem(itemId)) return [];
532
- const text = this.assistantTextByItem.get(itemId)?.trim();
533
- const timestamp = this.assistantTimestampByItem.get(itemId);
534
- if (!text || timestamp === void 0) return [];
535
- return [{
542
+ const delivery = this.createAsyncDelivery(itemId);
543
+ return delivery ? [{
536
544
  itemId,
537
- message: createAssistantAsyncMessage(this.params, text, itemId, timestamp)
538
- }];
545
+ message: delivery.message
546
+ }] : [];
539
547
  });
540
548
  }
541
549
  collectCompletedAssistantMessages(completedItemIds, options) {
@@ -616,6 +624,11 @@ var CodexAssistantProjection = class {
616
624
  if (phase) this.assistantPhaseByItem.set(item.id, phase);
617
625
  const delivery = readItemString(item, "delivery");
618
626
  if (delivery) this.assistantDeliveryByItem.set(item.id, delivery);
627
+ if (item.questions !== void 0) {
628
+ const questions = readCodexAsyncQuestions(item.questions);
629
+ if (questions && delivery === "async") this.assistantQuestionsByItem.set(item.id, questions);
630
+ else this.assistantQuestionsByItem.delete(item.id);
631
+ }
619
632
  }
620
633
  isCommentaryAssistantItem(itemId) {
621
634
  return this.assistantPhaseByItem.get(itemId) === "commentary";
@@ -755,7 +768,7 @@ var CodexAssistantProjection = class {
755
768
  if (!text?.trim() || timestamp === void 0) return;
756
769
  return {
757
770
  itemId,
758
- message: createAssistantAsyncMessage(this.params, text, itemId, timestamp),
771
+ message: createAssistantAsyncMessage(this.params, text, itemId, timestamp, this.assistantQuestionsByItem.get(itemId)),
759
772
  text
760
773
  };
761
774
  }
@@ -808,7 +821,7 @@ var CodexGeneratedMediaProjection = class {
808
821
  this.config = config;
809
822
  this.remote = remote;
810
823
  this.itemIds = /* @__PURE__ */ new Set();
811
- this.urlsByItemId = /* @__PURE__ */ new Map();
824
+ this.mediaByItemId = /* @__PURE__ */ new Map();
812
825
  this.gatewayMaterializedItemIds = /* @__PURE__ */ new Set();
813
826
  this.pendingMaterializationsByItemId = /* @__PURE__ */ new Map();
814
827
  }
@@ -818,6 +831,11 @@ var CodexGeneratedMediaProjection = class {
818
831
  async recordNative(item) {
819
832
  if (item?.type !== "imageGeneration") return;
820
833
  this.itemIds.add(item.id);
834
+ const savedPath = readItemString(item, "savedPath")?.trim();
835
+ if (savedPath) this.mediaByItemId.set(item.id, {
836
+ ...this.mediaByItemId.get(item.id),
837
+ savedPath
838
+ });
821
839
  const result = readItemString(item, "result");
822
840
  if (result) {
823
841
  await this.recordImage({
@@ -828,7 +846,6 @@ var CodexGeneratedMediaProjection = class {
828
846
  });
829
847
  return;
830
848
  }
831
- const savedPath = readItemString(item, "savedPath")?.trim();
832
849
  if (savedPath) {
833
850
  if (this.remote?.remoteWorkspaceRoot) {
834
851
  if (!this.remote.readFile) {
@@ -929,22 +946,53 @@ var CodexGeneratedMediaProjection = class {
929
946
  });
930
947
  }
931
948
  }
932
- buildToolMediaUrls(params) {
949
+ projectDelivery(params) {
950
+ const generatedUrls = /* @__PURE__ */ new Set();
951
+ const generatedUrlBySource = /* @__PURE__ */ new Map();
952
+ for (const { mediaUrl, savedPath } of this.mediaByItemId.values()) {
953
+ if (!mediaUrl) continue;
954
+ generatedUrls.add(mediaUrl);
955
+ generatedUrlBySource.set(normalizeMediaReferenceForComparison(mediaUrl), mediaUrl);
956
+ if (savedPath) generatedUrlBySource.set(normalizeMediaReferenceForComparison(savedPath), mediaUrl);
957
+ }
958
+ const sentMediaUrls = new Set(params.messagingToolSentMediaUrls);
959
+ const generatedUrlsByTarget = /* @__PURE__ */ new Map();
960
+ for (const delivery of params.confirmedMediaDeliveries ?? []) for (const sourceUrl of delivery.sourceUrls) {
961
+ const generatedUrl = generatedUrlBySource.get(normalizeMediaReferenceForComparison(sourceUrl));
962
+ if (!generatedUrl) continue;
963
+ if (delivery.kind === "sourceReply") generatedUrls.delete(generatedUrl);
964
+ else {
965
+ const targetUrls = generatedUrlsByTarget.get(delivery.target) ?? /* @__PURE__ */ new Set();
966
+ targetUrls.add(generatedUrl);
967
+ generatedUrlsByTarget.set(delivery.target, targetUrls);
968
+ sentMediaUrls.add(generatedUrl);
969
+ }
970
+ }
933
971
  const mediaUrls = new Set(params.toolMediaUrls?.map((url) => url.trim()).filter(Boolean) ?? []);
934
- if ((params.messagingToolSentMediaUrls?.length ?? 0) === 0) for (const mediaUrl of this.urlsByItemId.values()) mediaUrls.add(mediaUrl);
935
- return mediaUrls.size > 0 ? [...mediaUrls] : params.toolMediaUrls;
936
- }
937
- buildHostOwnedMediaUrls(params) {
938
- if ((params.messagingToolSentMediaUrls?.length ?? 0) > 0) return;
939
- const mediaUrls = [...this.urlsByItemId.values()];
940
- return mediaUrls.length > 0 ? mediaUrls : void 0;
972
+ for (const mediaUrl of generatedUrls) mediaUrls.add(mediaUrl);
973
+ return {
974
+ toolMediaUrls: mediaUrls.size > 0 ? [...mediaUrls] : params.toolMediaUrls,
975
+ hostOwnedToolMediaUrls: generatedUrls.size > 0 ? [...generatedUrls] : void 0,
976
+ messagingToolSentMediaUrls: [...sentMediaUrls],
977
+ messagingToolSentTargets: params.messagingToolSentTargets.map((target) => {
978
+ const aliases = generatedUrlsByTarget.get(target);
979
+ return aliases ? {
980
+ ...target,
981
+ mediaUrls: [.../* @__PURE__ */ new Set([...target.mediaUrls ?? [], ...aliases])]
982
+ } : target;
983
+ })
984
+ };
941
985
  }
942
986
  recordUrl(params) {
943
- if (this.urlsByItemId.has(params.itemId) && params.replaceExisting !== true) {
987
+ const existing = this.mediaByItemId.get(params.itemId);
988
+ if (existing?.mediaUrl && params.replaceExisting !== true) {
944
989
  this.itemIds.add(params.itemId);
945
990
  return;
946
991
  }
947
- this.urlsByItemId.set(params.itemId, params.mediaUrl);
992
+ this.mediaByItemId.set(params.itemId, {
993
+ ...existing,
994
+ mediaUrl: params.mediaUrl
995
+ });
948
996
  this.itemIds.add(params.itemId);
949
997
  }
950
998
  };
@@ -974,8 +1022,7 @@ var CodexReasoningProjection = class {
974
1022
  this.params = params;
975
1023
  this.emitAgentEvent = emitAgentEvent;
976
1024
  this.onNativePlanUpdate = onNativePlanUpdate;
977
- this.reasoningTextByGroup = /* @__PURE__ */ new Map();
978
- this.reasoningItemOrder = /* @__PURE__ */ new Map();
1025
+ this.reasoningTextByItem = /* @__PURE__ */ new Map();
979
1026
  this.planTextByItem = /* @__PURE__ */ new Map();
980
1027
  this.reasoningStarted = false;
981
1028
  this.reasoningEnded = false;
@@ -985,16 +1032,14 @@ var CodexReasoningProjection = class {
985
1032
  const delta = readStringField(params, "delta") ?? "";
986
1033
  if (!delta) return;
987
1034
  this.reasoningStarted = true;
988
- if (!this.reasoningItemOrder.has(itemId)) this.reasoningItemOrder.set(itemId, this.reasoningItemOrder.size);
1035
+ const item = this.reasoningTextByItem.get(itemId) ?? {
1036
+ summary: /* @__PURE__ */ new Map(),
1037
+ content: /* @__PURE__ */ new Map()
1038
+ };
1039
+ this.reasoningTextByItem.set(itemId, item);
989
1040
  const groupIndex = method === "item/reasoning/textDelta" ? readNonNegativeInteger(params, "contentIndex") ?? 0 : readNonNegativeInteger(params, "summaryIndex") ?? 0;
990
- const groupKey = `${method}\0${itemId}\0${groupIndex}`;
991
- const current = this.reasoningTextByGroup.get(groupKey);
992
- this.reasoningTextByGroup.set(groupKey, {
993
- itemId,
994
- method,
995
- index: groupIndex,
996
- text: `${current?.text ?? ""}${delta}`
997
- });
1041
+ const sections = method === "item/reasoning/textDelta" ? item.content : item.summary;
1042
+ sections.set(groupIndex, `${sections.get(groupIndex) ?? ""}${delta}`);
998
1043
  await this.params.onReasoningStream?.({
999
1044
  text: this.reasoningText(),
1000
1045
  isReasoningSnapshot: true
@@ -1034,10 +1079,27 @@ var CodexReasoningProjection = class {
1034
1079
  });
1035
1080
  this.emitPlanUpdate({
1036
1081
  explanation,
1082
+ ...params.explanationFormat === "plain" ? { explanationFormat: "plain" } : {},
1037
1083
  steps: plan
1038
1084
  }, source);
1039
1085
  }
1040
- recordItem(item) {
1086
+ async recordItem(item) {
1087
+ if (item?.type === "reasoning") {
1088
+ const previousText = this.reasoningText();
1089
+ this.reasoningTextByItem.set(item.id, {
1090
+ summary: readReasoningSections(item.summary),
1091
+ content: readReasoningSections(item.content)
1092
+ });
1093
+ const text = this.reasoningText();
1094
+ if (text !== previousText) {
1095
+ this.reasoningStarted = true;
1096
+ await this.params.onReasoningStream?.({
1097
+ text,
1098
+ isReasoningSnapshot: true
1099
+ });
1100
+ }
1101
+ return;
1102
+ }
1041
1103
  if (item?.type === "plan" && typeof item.text === "string" && item.text) {
1042
1104
  this.planTextByItem.set(item.id, item.text);
1043
1105
  this.emitPlanUpdate({
@@ -1055,7 +1117,7 @@ var CodexReasoningProjection = class {
1055
1117
  await this.params.onReasoningEnd?.();
1056
1118
  }
1057
1119
  reasoningText() {
1058
- return collectReasoningTextValues(this.reasoningTextByGroup, this.reasoningItemOrder).join("\n\n");
1120
+ return [...this.reasoningTextByItem.values()].flatMap(({ summary, content }) => [summary, content]).flatMap((sections) => [...sections].toSorted(([left], [right]) => left - right)).map(([, text]) => text).filter((text) => text.trim().length > 0).join("\n\n");
1059
1121
  }
1060
1122
  planText() {
1061
1123
  return this.turnPlanText ?? [...this.planTextByItem.values()].filter((text) => text.trim().length > 0).join("\n\n");
@@ -1069,6 +1131,7 @@ var CodexReasoningProjection = class {
1069
1131
  title: "Plan updated",
1070
1132
  source,
1071
1133
  ...params.explanation ? { explanation: params.explanation } : {},
1134
+ ...params.explanationFormat ? { explanationFormat: params.explanationFormat } : {},
1072
1135
  ...params.steps ? { steps: params.steps } : {}
1073
1136
  }
1074
1137
  });
@@ -1078,16 +1141,12 @@ function normalizePlanStepStatus(status) {
1078
1141
  if (status === "inProgress" || status === "in_progress") return "in_progress";
1079
1142
  return status === "completed" ? "completed" : "pending";
1080
1143
  }
1081
- function collectReasoningTextValues(groups, itemOrder) {
1082
- return [...groups.values()].toSorted((left, right) => {
1083
- const itemDelta = (itemOrder.get(left.itemId) ?? Number.MAX_SAFE_INTEGER) - (itemOrder.get(right.itemId) ?? Number.MAX_SAFE_INTEGER);
1084
- if (itemDelta !== 0) return itemDelta;
1085
- const methodDelta = reasoningMethodOrder(left.method) - reasoningMethodOrder(right.method);
1086
- return methodDelta !== 0 ? methodDelta : left.index - right.index;
1087
- }).map((group) => group.text).filter((text) => text.trim().length > 0);
1088
- }
1089
- function reasoningMethodOrder(method) {
1090
- return method === "item/reasoning/summaryTextDelta" ? 0 : 1;
1144
+ function readReasoningSections(value) {
1145
+ const sections = /* @__PURE__ */ new Map();
1146
+ if (Array.isArray(value)) value.forEach((text, index) => {
1147
+ if (typeof text === "string") sections.set(index, text);
1148
+ });
1149
+ return sections;
1091
1150
  }
1092
1151
  //#endregion
1093
1152
  //#region extensions/codex/src/app-server/event-projector-settlement.ts
@@ -1156,7 +1215,8 @@ function buildCodexMessagesSnapshot(params) {
1156
1215
  ...commentaryMessages,
1157
1216
  ...asyncMessages,
1158
1217
  ...(params.assistantMessages ?? []).map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:assistant:${itemId}`)),
1159
- ...params.toolMessages
1218
+ ...params.toolMessages,
1219
+ ...params.steeringMessages ?? []
1160
1220
  ].toSorted((left, right) => (asDateTimestampMs(left.timestamp) ?? 0) - (asDateTimestampMs(right.timestamp) ?? 0));
1161
1221
  messages.push(...visibleWorkMessages);
1162
1222
  if (params.lastAssistant) {
@@ -1166,6 +1226,7 @@ function buildCodexMessagesSnapshot(params) {
1166
1226
  const taint = { tainted: false };
1167
1227
  return messages.map((message) => projectAgentHarnessTranscriptMessageForDisplay({
1168
1228
  hidden: params.runParams.trigger === "memory",
1229
+ inputProvenance: params.runParams.inputProvenance,
1169
1230
  message: applyCodexTranscriptTaint(message, taint)
1170
1231
  }));
1171
1232
  }
@@ -1193,171 +1254,14 @@ function buildCodexSteeringMessagesSnapshot(params) {
1193
1254
  };
1194
1255
  }
1195
1256
  //#endregion
1196
- //#region extensions/codex/src/app-server/usage-limit-error.ts
1197
- /**
1198
- * Enriches Codex usage-limit failures with current rate-limit information and
1199
- * marks blocked auth profiles when Codex exposes a reset time.
1200
- */
1201
- const CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS = 5e3;
1202
- var CodexUsageLimitPromptError = class extends Error {
1203
- constructor(..._args) {
1204
- super(..._args);
1205
- this.status = 429;
1206
- }
1207
- };
1208
- function resolveCodexPromptError(source) {
1209
- const usageLimitMessage = formatCodexUsageLimitErrorMessage(source);
1210
- if (usageLimitMessage) return new CodexUsageLimitPromptError(usageLimitMessage);
1211
- const info = source.codexErrorInfo;
1212
- let status = info === "rateLimitExceeded" ? 429 : info === "serverOverloaded" ? 503 : info === "internalServerError" ? 500 : void 0;
1213
- if (isJsonObject(info)) for (const variant of [
1214
- "httpConnectionFailed",
1215
- "responseStreamConnectionFailed",
1216
- "responseStreamDisconnected",
1217
- "responseTooManyFailedAttempts"
1218
- ]) {
1219
- const detail = info[variant];
1220
- if (isJsonObject(detail) && typeof detail.httpStatusCode === "number") {
1221
- status = detail.httpStatusCode;
1222
- break;
1223
- }
1224
- }
1225
- return status === void 0 ? source.message ?? void 0 : Object.assign(new Error(source.message ?? "codex app-server error"), {
1226
- status,
1227
- ...info === "serverOverloaded" ? { code: "OVERLOADED" } : {}
1228
- });
1229
- }
1230
- /** Marks a Codex auth profile blocked until the reset time advertised by rate limits. */
1231
- async function markCodexAuthProfileBlockedFromRateLimits(params) {
1232
- const authProfileId = params.authProfileId?.trim();
1233
- if (!authProfileId || !params.params.authProfileStore) return;
1234
- const blockedUntil = resolveCodexUsageLimitResetAtMs(params.rateLimits);
1235
- if (!blockedUntil) return;
1236
- try {
1237
- await markAuthProfileBlockedUntil({
1238
- store: params.params.authProfileStore,
1239
- profileId: authProfileId,
1240
- blockedUntil,
1241
- source: "codex_rate_limits",
1242
- agentDir: params.params.agentDir,
1243
- runId: params.params.runId,
1244
- modelId: params.params.modelId
1245
- });
1246
- } catch (error) {
1247
- embeddedAgentLog.debug("failed to mark Codex auth profile blocked from app-server limits", {
1248
- authProfileId,
1249
- error: formatErrorMessage(error)
1250
- });
1251
- }
1252
- }
1253
- /** Formats a turn-start usage-limit error, refreshing rate limits when needed. */
1254
- async function formatCodexTurnStartUsageLimitError(params) {
1255
- return refreshCodexUsageLimitError({
1256
- client: params.client,
1257
- source: readCodexTurnStartUsageLimitErrorSource(params.client, params.error, params.errorNotification, params.rateLimitsRevisionBeforeTurnStart),
1258
- timeoutMs: params.timeoutMs,
1259
- signal: params.signal
1260
- });
1261
- }
1262
- /** Refreshes a generic prompt usage-limit message into a reset-aware message. */
1263
- async function refreshCodexUsageLimitPromptError(params) {
1264
- if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(params.message)) return;
1265
- return refreshCodexUsageLimitError({
1266
- client: params.client,
1267
- source: {
1268
- message: params.message,
1269
- codexErrorInfo: "usageLimitExceeded",
1270
- rateLimits: readRecentCodexRateLimits(params.client)
1271
- },
1272
- timeoutMs: params.timeoutMs,
1273
- signal: params.signal
1274
- });
1275
- }
1276
- async function refreshCodexUsageLimitError(params) {
1277
- const initialMessage = formatCodexUsageLimitErrorMessage(params.source);
1278
- if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(initialMessage)) return initialMessage ? {
1279
- message: initialMessage,
1280
- ...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
1281
- } : void 0;
1282
- const rateLimits = await readCodexRateLimitsFromAppServerForUsageLimitError({
1283
- client: params.client,
1284
- timeoutMs: params.timeoutMs,
1285
- signal: params.signal
1286
- });
1287
- if (!rateLimits) return initialMessage ? {
1288
- message: initialMessage,
1289
- ...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
1290
- } : void 0;
1291
- const message = formatCodexUsageLimitErrorMessage({
1292
- message: params.source.message,
1293
- codexErrorInfo: params.source.codexErrorInfo,
1294
- rateLimits,
1295
- rateLimitsAuthoritative: true
1296
- }) ?? initialMessage;
1297
- return message ? {
1298
- message,
1299
- rateLimitsForProfile: rateLimits
1300
- } : void 0;
1301
- }
1302
- async function readCodexRateLimitsFromAppServerForUsageLimitError(params) {
1303
- if (params.signal?.aborted) return;
1304
- try {
1305
- const rateLimits = await params.client.request(CODEX_CONTROL_METHODS.rateLimits, void 0, {
1306
- timeoutMs: resolveCodexUsageLimitRateLimitRefreshTimeoutMs(params.timeoutMs),
1307
- signal: params.signal
1308
- });
1309
- rememberCodexRateLimitsRead(params.client, rateLimits);
1310
- return rateLimits;
1311
- } catch (error) {
1312
- embeddedAgentLog.debug("codex app-server rate-limit refresh failed after usage-limit error", { error: formatErrorMessage(error) });
1313
- return;
1314
- }
1315
- }
1316
- function resolveCodexUsageLimitRateLimitRefreshTimeoutMs(timeoutMs) {
1317
- if (timeoutMs === void 0 || !Number.isFinite(timeoutMs) || timeoutMs <= 0) return CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS;
1318
- return Math.max(100, Math.min(timeoutMs, CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS));
1319
- }
1320
- function readCodexTurnStartUsageLimitErrorSource(client, error, errorNotification, rateLimitsRevisionBeforeTurnStart) {
1321
- const notificationError = readCodexErrorNotification(errorNotification);
1322
- const errorPayload = readCodexErrorPayload(error);
1323
- const rateLimits = errorPayload.rateLimits ?? readRecentCodexRateLimits(client);
1324
- const cacheUpdatedDuringTurnStart = rateLimitsRevisionBeforeTurnStart !== void 0 && readCodexRateLimitsRevision(client) > rateLimitsRevisionBeforeTurnStart;
1325
- return {
1326
- message: notificationError?.message ?? errorPayload.message ?? formatErrorMessage(error),
1327
- codexErrorInfo: notificationError?.codexErrorInfo ?? errorPayload.codexErrorInfo,
1328
- rateLimits,
1329
- rateLimitsTrustedForProfile: errorPayload.rateLimits !== void 0 || cacheUpdatedDuringTurnStart
1330
- };
1331
- }
1332
- function readCodexErrorNotification(notification) {
1333
- if (notification?.method !== "error" || !isJsonObject(notification.params)) return;
1334
- const error = notification.params.error;
1335
- return isJsonObject(error) ? {
1336
- message: readStringField(error, "message"),
1337
- codexErrorInfo: error.codexErrorInfo
1338
- } : void 0;
1339
- }
1340
- function readCodexErrorPayload(error) {
1341
- const message = error instanceof Error ? error.message : void 0;
1342
- if (!error || typeof error !== "object" || !("data" in error)) return { message };
1343
- const data = error.data;
1344
- if (!isJsonObject(data)) return { message };
1345
- const nestedError = isJsonObject(data.error) ? data.error : data;
1346
- const rateLimits = nestedError.rateLimits ?? data.rateLimits;
1347
- return {
1348
- message: readStringField(nestedError, "message") ?? message,
1349
- codexErrorInfo: nestedError.codexErrorInfo,
1350
- rateLimits
1351
- };
1352
- }
1353
- //#endregion
1354
1257
  //#region extensions/codex/src/app-server/event-projector-terminal-failure.ts
1355
1258
  var CodexTerminalFailureProjection = class {
1356
1259
  constructor() {
1357
1260
  this.promptErrorSource = null;
1358
1261
  }
1359
1262
  record(params) {
1360
- this.providerRefusal ??= readCodexProviderRefusal(params.message, params.codexErrorInfo);
1263
+ const refusal = readCodexProviderRefusal(params.message, params.codexErrorInfo, params);
1264
+ if (!this.providerRefusal || refusal?.category === "misalignment" && this.providerRefusal.category === "misalignment" && params.misalignment != null) this.providerRefusal = refusal;
1361
1265
  if (this.providerRefusal) return;
1362
1266
  this.promptError = resolveCodexPromptError({
1363
1267
  message: params.message,
@@ -1417,6 +1321,7 @@ var CodexTranscriptCheckpoint = class {
1417
1321
  const message = entry.read();
1418
1322
  return message ? [projectAgentHarnessTranscriptMessageForDisplay({
1419
1323
  hidden: this.params.trigger === "memory",
1324
+ inputProvenance: this.params.inputProvenance,
1420
1325
  message: applyCodexTranscriptTaint(message, taint)
1421
1326
  })] : [];
1422
1327
  });
@@ -1486,7 +1391,7 @@ var CodexTurnProjection = class {
1486
1391
  trajectoryRecorder: options.trajectoryRecorder,
1487
1392
  checkpointMessage: this.transcriptCheckpoint.enqueue
1488
1393
  });
1489
- this.eventProjection = new CodexEventProjection(threadId, turnId, (event) => this.emitAgentEvent(event), this.toolProgressProjection, this.toolTranscriptProjection, options.onNativeToolResultRecorded);
1394
+ this.eventProjection = new CodexEventProjection(params.provider, threadId, turnId, (event) => this.emitAgentEvent(event), this.toolProgressProjection, this.toolTranscriptProjection, options.onNativeToolResultRecorded);
1490
1395
  this.assistantProjection = new CodexAssistantProjection(this.settlement.params, (event) => this.emitAgentEvent(event), (text) => this.toolProgressProjection.matchesEcho(text), this.transcriptCheckpoint.nextTimestamp, this.transcriptCheckpoint.enqueueCommentary);
1491
1396
  this.reasoningProjection = new CodexReasoningProjection(this.settlement.params, (event) => this.emitAgentEvent(event), options.onNativePlanUpdate);
1492
1397
  }
@@ -1543,6 +1448,7 @@ var CodexTurnProjection = class {
1543
1448
  asyncMessages,
1544
1449
  commentaryMessages,
1545
1450
  toolMessages: this.toolTranscriptProjection.transcriptMessages,
1451
+ steeringMessages: options?.steeringMessages,
1546
1452
  lastAssistant,
1547
1453
  turnTainted
1548
1454
  });
@@ -1557,7 +1463,8 @@ var CodexTurnProjection = class {
1557
1463
  });
1558
1464
  const toolMetas = this.toolProgressProjection.toolMetas;
1559
1465
  const hadPotentialSideEffects = toolTelemetry.didSendViaMessagingTool || Boolean(toolTelemetry.successfulCronAdds || toolTelemetry.acceptedSessionSpawns?.length) || this.generatedMediaProjection.hasGeneratedMedia() || this.toolProgressProjection.hasPotentialSideEffects;
1560
- const sentMediaUrls = new Set(toolTelemetry.messagingToolSentMediaUrls.map((url) => url.trim()));
1466
+ const mediaDelivery = this.generatedMediaProjection.projectDelivery(toolTelemetry);
1467
+ const sentMediaUrls = new Set(mediaDelivery.messagingToolSentMediaUrls.map((url) => url.trim()));
1561
1468
  const toolAutoDeliveryMediaUrls = toolTelemetry.toolAutoDeliveryMediaUrls?.filter((url) => !sentMediaUrls.has(url.trim()));
1562
1469
  const result = {
1563
1470
  terminal: attemptTerminal.normalize({
@@ -1581,12 +1488,9 @@ var CodexTurnProjection = class {
1581
1488
  didDeliverSourceReplyViaMessageTool: toolTelemetry.didDeliverSourceReplyViaMessageTool === true,
1582
1489
  sourceReplyDelivered: toolTelemetry.sourceReplyDelivered,
1583
1490
  messagingToolSentTexts: toolTelemetry.messagingToolSentTexts,
1584
- messagingToolSentMediaUrls: toolTelemetry.messagingToolSentMediaUrls,
1585
- messagingToolSentTargets: toolTelemetry.messagingToolSentTargets,
1491
+ ...mediaDelivery,
1586
1492
  messagingToolSourceReplyPayloads: toolTelemetry.messagingToolSourceReplyPayloads ?? [],
1587
1493
  heartbeatToolResponse: toolTelemetry.heartbeatToolResponse,
1588
- toolMediaUrls: this.generatedMediaProjection.buildToolMediaUrls(toolTelemetry),
1589
- hostOwnedToolMediaUrls: this.generatedMediaProjection.buildHostOwnedMediaUrls(toolTelemetry),
1590
1494
  toolAudioAsVoice: toolTelemetry.toolAudioAsVoice,
1591
1495
  successfulCronAdds: toolTelemetry.successfulCronAdds,
1592
1496
  acceptedSessionSpawns: toolTelemetry.acceptedSessionSpawns,
@@ -1617,6 +1521,7 @@ var CodexTurnProjection = class {
1617
1521
  };
1618
1522
  //#endregion
1619
1523
  //#region extensions/codex/src/app-server/event-projector.ts
1524
+ const optedOutNotificationMethods = new Set(CODEX_APP_SERVER_OPT_OUT_NOTIFICATION_METHODS);
1620
1525
  var CodexAppServerEventProjector = class extends CodexTurnProjection {
1621
1526
  getCompletedTurnStatus() {
1622
1527
  return this.completedTurn?.status;
@@ -1689,6 +1594,7 @@ var CodexAppServerEventProjector = class extends CodexTurnProjection {
1689
1594
  this.assistantProjection.handleNotification(notification.method, params);
1690
1595
  switch (notification.method) {
1691
1596
  case "item/agentMessage/delta":
1597
+ if (readStringField(params, "delta")) this.eventProjection.markSafetyBufferingAssistantStarted();
1692
1598
  await this.assistantProjection.handleAssistantDelta(params);
1693
1599
  break;
1694
1600
  case "item/reasoning/summaryTextDelta":
@@ -1752,6 +1658,9 @@ var CodexAppServerEventProjector = class extends CodexTurnProjection {
1752
1658
  case "model/rerouted":
1753
1659
  this.eventProjection.handleModelRerouted(params);
1754
1660
  break;
1661
+ case "model/safetyBuffering/updated":
1662
+ this.eventProjection.handleSafetyBuffering(params);
1663
+ break;
1755
1664
  case "error": {
1756
1665
  this.usageProjection.invalidateContext();
1757
1666
  if (params.willRetry === true) {
@@ -1759,29 +1668,29 @@ var CodexAppServerEventProjector = class extends CodexTurnProjection {
1759
1668
  break;
1760
1669
  }
1761
1670
  const codexErrorInfo = isJsonObject(params.error) ? params.error.codexErrorInfo : void 0;
1671
+ this.eventProjection.handleCyberPolicyError(codexErrorInfo, this.params.modelId);
1762
1672
  const compactionFailure = codexErrorInfo === "other" && this.isCompacting();
1763
1673
  this.settledTurnFailureFinalizationAllowed = codexErrorInfo === "serverOverloaded" || compactionFailure;
1764
1674
  this.terminalFailure.record({
1765
1675
  message: readCodexErrorNotificationMessage(params),
1766
1676
  codexErrorInfo,
1677
+ misalignment: isJsonObject(params.error) ? params.error.misalignment : void 0,
1678
+ nativeThreadId: this.threadId,
1679
+ nativeTurnId: this.turnId,
1767
1680
  rateLimits: this.options.readRecentRateLimits?.(),
1768
1681
  fallbackMessage: "codex app-server error",
1769
1682
  promptErrorSource: compactionFailure ? "compaction" : "prompt"
1770
1683
  });
1771
1684
  break;
1772
1685
  }
1773
- case "thread/compacted":
1774
1686
  case "turn/started":
1775
- case "turn/diff/updated":
1776
1687
  case "item/reasoning/summaryPartAdded":
1777
1688
  case "item/commandExecution/terminalInteraction":
1778
- case "item/fileChange/outputDelta":
1779
1689
  case "item/fileChange/patchUpdated":
1780
1690
  case "item/mcpToolCall/progress":
1781
1691
  case "model/verification":
1782
- case "turn/moderationMetadata":
1783
- case "model/safetyBuffering/updated": break;
1784
- default: this.diagnostics.warnUnknownEvent(notification, params);
1692
+ case "turn/moderationMetadata": break;
1693
+ default: if (!optedOutNotificationMethods.has(notification.method)) this.diagnostics.warnUnknownEvent(notification, params);
1785
1694
  }
1786
1695
  if (!this.projectionClosed && [
1787
1696
  "item/started",
@@ -1789,6 +1698,23 @@ var CodexAppServerEventProjector = class extends CodexTurnProjection {
1789
1698
  "rawResponseItem/completed"
1790
1699
  ].includes(notification.method)) await this.settlement.project("transcript/checkpoint", () => this.transcriptCheckpoint.flush());
1791
1700
  }
1701
+ recordUserInputResponse(params) {
1702
+ if (this.projectionClosed) return;
1703
+ this.toolTranscriptProjection.recordToolCall({
1704
+ id: params.itemId,
1705
+ name: "request_user_input",
1706
+ arguments: { questions: params.questions }
1707
+ });
1708
+ this.toolTranscriptProjection.recordToolResult({
1709
+ id: params.itemId,
1710
+ name: "request_user_input",
1711
+ text: JSON.stringify({
1712
+ response: params.response,
1713
+ request: { questions: params.questions }
1714
+ }),
1715
+ isError: false
1716
+ });
1717
+ }
1792
1718
  recordDynamicToolCall(params) {
1793
1719
  this.toolTranscriptProjection.recordDynamicToolCall(params);
1794
1720
  }
@@ -1798,7 +1724,8 @@ var CodexAppServerEventProjector = class extends CodexTurnProjection {
1798
1724
  if (update) {
1799
1725
  const projected = {
1800
1726
  plan: update.steps,
1801
- ...update.explanation ? { explanation: update.explanation } : {}
1727
+ ...update.explanation ? { explanation: update.explanation } : {},
1728
+ ...update.explanationFormat ? { explanationFormat: update.explanationFormat } : {}
1802
1729
  };
1803
1730
  await this.reasoningProjection.handleTurnPlanUpdated(projected, "openclaw");
1804
1731
  }
@@ -1825,8 +1752,9 @@ var CodexAppServerEventProjector = class extends CodexTurnProjection {
1825
1752
  }
1826
1753
  async handleItemStarted(params) {
1827
1754
  const item = readItem(params.item);
1755
+ if (item?.type === "agentMessage" && item.text) this.eventProjection.markSafetyBufferingAssistantStarted();
1828
1756
  const itemId = item?.id ?? readStringField(params, "itemId");
1829
- this.assistantProjection.recordItemStarted(item, itemId);
1757
+ await this.assistantProjection.recordItemStarted(item, itemId);
1830
1758
  if (itemId) this.activeItemIds.add(itemId);
1831
1759
  this.recordNativeToolOutcome(item);
1832
1760
  if (item?.type === "contextCompaction" && itemId) {
@@ -1874,10 +1802,12 @@ var CodexAppServerEventProjector = class extends CodexTurnProjection {
1874
1802
  }
1875
1803
  async handleItemCompleted(params) {
1876
1804
  const item = readItem(params.item);
1805
+ const itemId = item?.id ?? readStringField(params, "itemId");
1806
+ if (item?.type === "contextCompaction" && itemId && this.completedItemIds.has(itemId)) return;
1807
+ if (item?.type === "agentMessage" && item.text) this.eventProjection.markSafetyBufferingAssistantStarted();
1877
1808
  this.diagnostics.warnUnknownItemStatus(item);
1878
1809
  this.recordNativeToolOutcome(item);
1879
1810
  this.nativeToolLifecycleProjector.clearTerminalPresentationForNativeItem(item);
1880
- const itemId = item?.id ?? readStringField(params, "itemId");
1881
1811
  if (itemId) {
1882
1812
  this.activeItemIds.delete(itemId);
1883
1813
  this.completedItemIds.add(itemId);
@@ -1886,7 +1816,7 @@ var CodexAppServerEventProjector = class extends CodexTurnProjection {
1886
1816
  const asyncMessage = this.assistantProjection.recordItemCompleted(item, itemId, this.activeItemIds);
1887
1817
  if (asyncMessage) await this.asyncDeliveryProjection.deliver(asyncMessage);
1888
1818
  if (this.projectionClosed) return;
1889
- this.reasoningProjection.recordItem(item);
1819
+ await this.reasoningProjection.recordItem(item);
1890
1820
  await this.settlement.project("media_projection", () => this.generatedMediaProjection.recordNative(item));
1891
1821
  if (this.projectionClosed) return;
1892
1822
  if (item?.type === "contextCompaction" && itemId) {
@@ -1944,17 +1874,22 @@ var CodexAppServerEventProjector = class extends CodexTurnProjection {
1944
1874
  });
1945
1875
  }
1946
1876
  async handleTurnCompleted(params) {
1947
- const turn = readCodexTurn(params.turn);
1877
+ const turn = readCodexTurnCompletedNotification(params)?.turn;
1948
1878
  if (!turn || turn.id !== this.turnId) return;
1949
1879
  this.completedTurn = turn;
1880
+ this.eventProjection.endSafetyBuffering();
1950
1881
  const compactionFailure = turn.status === "failed" && (this.terminalFailure.promptErrorSource === "compaction" || turn.error?.codexErrorInfo === "other" && this.isCompacting());
1951
1882
  this.settledTurnFailureFinalizationAllowed = turn.status === "failed" && (turn.error?.codexErrorInfo === "serverOverloaded" || compactionFailure);
1952
1883
  if (turn.status !== "completed") this.usageProjection.invalidateContext();
1953
1884
  if (turn.status === "failed") {
1954
1885
  const codexErrorInfo = turn.error?.codexErrorInfo;
1886
+ this.eventProjection.handleCyberPolicyError(codexErrorInfo, this.params.modelId);
1955
1887
  this.terminalFailure.record({
1956
1888
  message: turn.error?.message,
1957
1889
  codexErrorInfo,
1890
+ misalignment: turn.error?.misalignment,
1891
+ nativeThreadId: this.threadId,
1892
+ nativeTurnId: this.turnId,
1958
1893
  rateLimits: this.options.readRecentRateLimits?.(),
1959
1894
  fallbackMessage: "codex app-server turn failed",
1960
1895
  promptErrorSource: compactionFailure ? "compaction" : "prompt"
@@ -1968,7 +1903,7 @@ var CodexAppServerEventProjector = class extends CodexTurnProjection {
1968
1903
  this.eventProjection.emitCompactionEnd(itemId, false);
1969
1904
  }
1970
1905
  }
1971
- const turnItems = turn.items ?? [];
1906
+ const turnItems = turn.items;
1972
1907
  const unsettledAsyncDeliveries = this.asyncDeliveryProjection.pending();
1973
1908
  const lastToolItem = turnItems.findLast((item) => matchesCodexSnapshotTurn(item, this.turnId) && (item.type === "dynamicToolCall" || shouldClearTerminalPresentationForNativeItem(item)));
1974
1909
  if (lastToolItem?.type !== "dynamicToolCall") this.nativeToolLifecycleProjector.clearTerminalPresentationForNativeItem(lastToolItem);
@@ -1978,7 +1913,7 @@ var CodexAppServerEventProjector = class extends CodexTurnProjection {
1978
1913
  const asyncMessage = this.assistantProjection.recordSnapshotItem(item);
1979
1914
  if (asyncMessage) await this.asyncDeliveryProjection.deliver(asyncMessage);
1980
1915
  if (this.projectionClosed) return;
1981
- this.reasoningProjection.recordItem(item);
1916
+ await this.reasoningProjection.recordItem(item);
1982
1917
  await this.settlement.project("media_projection", () => this.generatedMediaProjection.recordNative(item));
1983
1918
  if (this.projectionClosed) return;
1984
1919
  this.toolProgressProjection.recordToolMeta(item);
@@ -2011,6 +1946,7 @@ var CodexAppServerEventProjector = class extends CodexTurnProjection {
2011
1946
  async handleRawResponseItemCompleted(params) {
2012
1947
  const item = isJsonObject(params.item) ? params.item : void 0;
2013
1948
  if (!item) return;
1949
+ if (item.role === "assistant" && extractRawAssistantText(item)) this.eventProjection.markSafetyBufferingAssistantStarted();
2014
1950
  this.toolTranscriptProjection.recordRawNativeToolItem(item);
2015
1951
  this.assistantProjection.handleRawResponseItemCompleted(item, this.activeItemIds);
2016
1952
  await this.settlement.project("media_projection", () => this.generatedMediaProjection.recordRaw(item));
@@ -2282,50 +2218,31 @@ const MAX_USER_INPUT_HEADER = 64;
2282
2218
  const MAX_USER_INPUT_TEXT = 4096;
2283
2219
  /** Creates the single per-turn owner for Codex operator input. */
2284
2220
  function createCodexUserInputBridge(params) {
2285
- const jobs = [];
2286
- let activeCompletion;
2221
+ const cleanup = new AbortController();
2222
+ const bridgeSignal = params.signal ? AbortSignal.any([params.signal, cleanup.signal]) : cleanup.signal;
2223
+ let completion = Promise.resolve();
2287
2224
  const inputSessionKey = params.paramsForRun.sessionKey ?? params.paramsForRun.sessionId;
2288
- const pump = () => {
2289
- const job = jobs[0];
2290
- if (activeCompletion || !job) return;
2291
- activeCompletion = job.run(job.abort.signal).catch((error) => {
2292
- embeddedAgentLog.warn("failed to bridge codex operator input", { error });
2293
- return job.abort.signal.aborted ? job.cancelValue : job.failureValue;
2294
- }).then((value) => {
2295
- job.resolve(job.abort.signal.aborted ? job.cancelValue : value ?? job.failureValue);
2296
- }).finally(() => {
2297
- if (jobs[0] === job) jobs.shift();
2298
- activeCompletion = void 0;
2299
- pump();
2300
- });
2301
- };
2302
- const enqueue = (definition) => {
2303
- if (params.signal?.aborted) return Promise.resolve(definition.cancelValue);
2304
- return new Promise((resolve) => {
2305
- jobs.push({
2306
- ...definition,
2307
- resolve
2308
- });
2309
- pump();
2225
+ const enqueue = (job, requestSignal) => {
2226
+ const signal = requestSignal ? AbortSignal.any([bridgeSignal, requestSignal]) : bridgeSignal;
2227
+ if (signal.aborted) return Promise.resolve(job.cancelValue);
2228
+ return new Promise((resolve, reject) => {
2229
+ const onAbort = () => resolve(job.cancelValue);
2230
+ signal.addEventListener("abort", onAbort, { once: true });
2231
+ completion = completion.then(async () => {
2232
+ signal.removeEventListener("abort", onAbort);
2233
+ if (signal.aborted) return;
2234
+ let value;
2235
+ try {
2236
+ value = await job.run(signal);
2237
+ } catch (error) {
2238
+ if (!signal.aborted) embeddedAgentLog.warn("failed to bridge codex operator input", { error });
2239
+ }
2240
+ const response = signal.aborted ? job.cancelValue : value ?? job.failureValue;
2241
+ if (!(signal.reason instanceof CodexServerRequestResolvedError)) job.onResponse?.(response);
2242
+ resolve(response);
2243
+ }).catch(reject);
2310
2244
  });
2311
2245
  };
2312
- const cancelJob = (job) => {
2313
- const index = jobs.indexOf(job);
2314
- if (index < 0) return;
2315
- if (index === 0) {
2316
- job.abort.abort(/* @__PURE__ */ new Error("Codex operator input request cancelled"));
2317
- return;
2318
- }
2319
- jobs.splice(index, 1);
2320
- job.resolve(job.cancelValue);
2321
- };
2322
- const cancelPending = async () => {
2323
- const pendingCompletion = activeCompletion;
2324
- const queuedJobs = jobs.splice(1);
2325
- for (const job of queuedJobs) job.resolve(job.cancelValue);
2326
- jobs[0]?.abort.abort(/* @__PURE__ */ new Error("Codex operator input request cancelled"));
2327
- await pendingCompletion;
2328
- };
2329
2246
  const execute = (input, timeoutMs, signal) => agentHarnessStructuredInput.run({
2330
2247
  input,
2331
2248
  sessionKey: inputSessionKey,
@@ -2341,9 +2258,8 @@ function createCodexUserInputBridge(params) {
2341
2258
  urlIntro: "Codex needs confirmation:"
2342
2259
  }
2343
2260
  });
2344
- params.signal?.addEventListener("abort", () => void cancelPending(), { once: true });
2345
2261
  return {
2346
- async handleRequest(request) {
2262
+ async handleRequest(request, requestSignal) {
2347
2263
  const requestParams = readUserInputParams(request.params);
2348
2264
  if (!requestParams || requestParams.threadId !== params.threadId || requestParams.turnId !== params.turnId) return;
2349
2265
  if (requestParams.questions.length === 0) return emptyUserInputResponse();
@@ -2351,47 +2267,38 @@ function createCodexUserInputBridge(params) {
2351
2267
  const input = compileUserInputQuestions(requestParams.questions);
2352
2268
  const cancelValue = emptyUserInputResponse();
2353
2269
  return await enqueue({
2354
- requestId: request.id,
2355
- abort: new AbortController(),
2356
2270
  cancelValue,
2357
2271
  failureValue: cancelValue,
2272
+ onResponse: requestParams.questions.some((question) => question.isSecret) ? void 0 : (response) => params.onOrdinaryResponse?.({
2273
+ itemId: requestParams.itemId,
2274
+ questions: requestParams.questions,
2275
+ response
2276
+ }),
2358
2277
  run: async (signal) => {
2359
2278
  const result = await execute(input, timeoutMs, signal);
2360
2279
  return result.status === "answered" ? gatewayAnswersToCodexResponse(result.answers) : cancelValue;
2361
2280
  }
2362
- });
2281
+ }, requestSignal);
2363
2282
  },
2364
- async handleElicitationRequest(request) {
2283
+ async handleElicitationRequest(request, requestSignal) {
2365
2284
  if (readOwnDataString(request.params, "threadId") !== params.threadId) return;
2366
2285
  const requestSnapshot = agentHarnessStructuredInput.snapshot(request.params);
2367
- if (!agentHarnessStructuredInput.isRecord(requestSnapshot)) {
2368
- const cancelValue = createCodexElicitationResponse("cancel");
2369
- return await enqueue({
2370
- requestId: request.id,
2371
- abort: new AbortController(),
2372
- cancelValue,
2373
- failureValue: declineElicitation("OpenClaw could not handle this elicitation."),
2374
- run: async (signal) => {
2375
- const result = await execute({
2376
- kind: "unsupported",
2377
- message: "OpenClaw declined a malformed or over-limit MCP elicitation request."
2378
- }, params.paramsForRun.timeoutMs ?? DEFAULT_USER_INPUT_TIMEOUT_MS, signal);
2379
- return result.status === "unsupported" ? declineElicitation(result.message) : cancelValue;
2380
- }
2381
- });
2382
- }
2383
- if (readOwnDataString(requestSnapshot, "threadId") !== params.threadId) return;
2384
- const { compileCodexOrdinaryElicitation } = await import("./elicitation-input-DPoK8F7w.js");
2385
- const compiled = compileCodexOrdinaryElicitation({
2286
+ if (agentHarnessStructuredInput.isRecord(requestSnapshot) && readOwnDataString(requestSnapshot, "threadId") !== params.threadId) return;
2287
+ const { compileCodexOrdinaryElicitation } = await import("./elicitation-input-DPoK8F7w.mjs");
2288
+ const compiled = agentHarnessStructuredInput.isRecord(requestSnapshot) ? compileCodexOrdinaryElicitation({
2386
2289
  snapshot: requestSnapshot,
2387
2290
  turnId: params.turnId
2388
- });
2291
+ }) : {
2292
+ kind: "compiled",
2293
+ input: {
2294
+ kind: "unsupported",
2295
+ message: "OpenClaw declined a malformed or over-limit MCP elicitation request."
2296
+ }
2297
+ };
2389
2298
  if (compiled.kind === "ignored") return;
2390
2299
  const cancelValue = createCodexElicitationResponse("cancel");
2391
2300
  const timeoutMs = params.paramsForRun.timeoutMs ?? DEFAULT_USER_INPUT_TIMEOUT_MS;
2392
2301
  return await enqueue({
2393
- requestId: request.id,
2394
- abort: new AbortController(),
2395
2302
  cancelValue,
2396
2303
  failureValue: declineElicitation("OpenClaw could not handle this elicitation."),
2397
2304
  run: async (signal) => {
@@ -2400,20 +2307,15 @@ function createCodexUserInputBridge(params) {
2400
2307
  const content = compiled.input.kind === "ready" && compiled.input.plan.kind === "url" ? null : result.content;
2401
2308
  return createCodexElicitationResponse("accept", content);
2402
2309
  }
2403
- if (result.status === "declined") return declineElicitation(result.message);
2404
- if (result.status === "unsupported") return declineElicitation(result.message);
2310
+ if (result.status === "declined" || result.status === "unsupported") return declineElicitation(result.message);
2405
2311
  return cancelValue;
2406
2312
  }
2407
- });
2313
+ }, requestSignal);
2408
2314
  },
2409
- handleNotification(notification) {
2410
- if (notification.method !== "serverRequest/resolved" || !isJsonObject(notification.params)) return;
2411
- const requestId = readRequestId(notification.params);
2412
- if (requestId === void 0 || readOwnDataString(notification.params, "threadId") !== params.threadId) return;
2413
- const job = jobs.find((candidate) => typeof candidate.requestId === typeof requestId && candidate.requestId === requestId);
2414
- if (job) cancelJob(job);
2415
- },
2416
- cancelPending
2315
+ async cancelPending() {
2316
+ cleanup.abort(/* @__PURE__ */ new Error("Codex operator input request cancelled"));
2317
+ await completion;
2318
+ }
2417
2319
  };
2418
2320
  }
2419
2321
  function compileUserInputQuestions(questions) {
@@ -2439,6 +2341,7 @@ function readUserInputParams(value) {
2439
2341
  return {
2440
2342
  threadId,
2441
2343
  turnId,
2344
+ itemId,
2442
2345
  questions: parsed,
2443
2346
  isBlocking: readValue(snapshot, "isBlocking") !== false
2444
2347
  };
@@ -2493,11 +2396,6 @@ function readOwnDataString(value, key) {
2493
2396
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
2494
2397
  return descriptor && "value" in descriptor && typeof descriptor.value === "string" ? descriptor.value : void 0;
2495
2398
  }
2496
- function readRequestId(record) {
2497
- const descriptor = Object.getOwnPropertyDescriptor(record, "requestId");
2498
- const value = descriptor && "value" in descriptor ? descriptor.value : void 0;
2499
- return typeof value === "string" || typeof value === "number" ? value : void 0;
2500
- }
2501
2399
  function gatewayAnswersToCodexResponse(answers) {
2502
2400
  return { answers: Object.fromEntries(Object.entries(answers).map(([questionId, values]) => [questionId, { answers: values }])) };
2503
2401
  }
@@ -2509,7 +2407,7 @@ function declineElicitation(message) {
2509
2407
  }
2510
2408
  //#endregion
2511
2409
  //#region extensions/codex/src/app-server/run-attempt-active-turn.ts
2512
- function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notifications, turn) {
2410
+ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notifications, { turn, upstreamUserText }) {
2513
2411
  const { prompt, state: resourceState, projectorRef, trajectoryRecorder, pendingNativePreToolUseFailures } = resources;
2514
2412
  const { context, turnState } = prompt;
2515
2413
  const { runtime, attemptTools, hookContext } = context;
@@ -2536,6 +2434,31 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2536
2434
  provider: runtimeModelSelection.provider
2537
2435
  }
2538
2436
  } : dynamicToolParams;
2437
+ const hostPrepareReplyMedia = params.hostCapabilities.prepareReplyMedia;
2438
+ const remoteWorkspaceRoot = connection.appServer.remoteWorkspaceRoot;
2439
+ const replyMediaClient = resourceState.client;
2440
+ const prepareReplyMedia = hostPrepareReplyMedia && remoteWorkspaceRoot ? async (content, transferSignal) => hostPrepareReplyMedia({
2441
+ ...content,
2442
+ workspaceRoot: remoteWorkspaceRoot,
2443
+ signal: runAbortController.signal,
2444
+ readWorkspaceFile: async (relativePath, { maxBytes, signal }) => {
2445
+ connection.assertCurrent();
2446
+ const file = await readBoundedCodexRemoteWorkspaceFile({
2447
+ client: replyMediaClient,
2448
+ path: mapCodexAppServerRemoteWorkspacePath({
2449
+ value: path.resolve(params.workspaceDir, relativePath),
2450
+ localWorkspaceRoot: params.workspaceDir,
2451
+ remoteWorkspaceRoot
2452
+ }),
2453
+ workspaceRoot: remoteWorkspaceRoot,
2454
+ maxBytes,
2455
+ signal: transferSignal ? AbortSignal.any([signal, transferSignal]) : signal,
2456
+ timeoutMs: connection.appServer.requestTimeoutMs
2457
+ });
2458
+ connection.assertCurrent();
2459
+ return Buffer.from(file.dataBase64, "base64");
2460
+ }
2461
+ }) : void 0;
2539
2462
  const progressCardTool = toolBridge.availableTools.find((tool) => tool.name === "progress_card");
2540
2463
  let nativePlanUpdateOrdinal = 0;
2541
2464
  const prepareNativeMcpAppResultDetails = createCodexNativeMcpAppResultDetailsPreparer({
@@ -2547,19 +2470,6 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2547
2470
  eventEmitted: false,
2548
2471
  needsTerminalSnapshot: false
2549
2472
  };
2550
- emitExecutionPhaseOnce("turn_accepted", { phase: "turn_accepted" });
2551
- userInputBridgeRef.current = createCodexUserInputBridge({
2552
- paramsForRun: params,
2553
- threadId: resourceState.thread.threadId,
2554
- turnId: activeTurnId,
2555
- signal: runAbortController.signal
2556
- });
2557
- trajectoryRecorder?.recordEvent("prompt.submitted", {
2558
- threadId: resourceState.thread.threadId,
2559
- turnId: activeTurnId,
2560
- prompt: turnState.codexTurnPromptText,
2561
- imagesCount: params.images?.length ?? 0
2562
- });
2563
2473
  projectorRef.current = new CodexAppServerEventProjector({
2564
2474
  ...projectionParams,
2565
2475
  onAgentEvent: (event) => {
@@ -2576,7 +2486,17 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2576
2486
  asyncUserMessageAllowed: params.disableTools !== true && (params.toolsAllow === void 0 || toolBridge.availableTools.some((tool) => tool.name === "message")),
2577
2487
  onAsyncDelivery: async (delivery) => {
2578
2488
  return await codexTranscriptMirrorRuntime.deliverAsyncMessageBestEffort({
2579
- params: projectionParams,
2489
+ params: prepareReplyMedia && projectionParams.onBlockReply ? {
2490
+ ...projectionParams,
2491
+ onBlockReply: async (payload, options) => {
2492
+ const prepared = await prepareReplyMedia({
2493
+ kind: "payload",
2494
+ payload
2495
+ });
2496
+ if (prepared.kind !== "payload") throw new Error("Reply media preparation returned the wrong result kind");
2497
+ await projectionParams.onBlockReply?.(prepared.payload, options);
2498
+ }
2499
+ } : projectionParams,
2580
2500
  cwd: effectiveCwd,
2581
2501
  threadId: resourceState.thread.threadId,
2582
2502
  turnId: activeTurnId,
@@ -2587,9 +2507,9 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2587
2507
  runAbortSignal: runAbortController.signal,
2588
2508
  remoteWorkspaceRoot: connection.appServer.remoteWorkspaceRoot,
2589
2509
  remoteWorkspaceRequestTimeoutMs: connection.appServer.requestTimeoutMs,
2590
- readRemoteWorkspaceFile: ({ path, maxBytes, signal, timeoutMs }) => readBoundedCodexRemoteWorkspaceFile({
2510
+ readRemoteWorkspaceFile: ({ path: remotePath, maxBytes, signal, timeoutMs }) => readBoundedCodexRemoteWorkspaceFile({
2591
2511
  client: resourceState.client,
2592
- path,
2512
+ path: remotePath,
2593
2513
  maxBytes,
2594
2514
  signal,
2595
2515
  timeoutMs
@@ -2611,7 +2531,7 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2611
2531
  }
2612
2532
  } } : {},
2613
2533
  ...prepareNativeMcpAppResultDetails ? { prepareNativeMcpAppResultDetails } : {},
2614
- upstreamUserText: turnState.codexTurnPromptText,
2534
+ upstreamUserText,
2615
2535
  onContextCompacted: async () => {
2616
2536
  computerContextEpoch.value += 1;
2617
2537
  delete computerContextEpoch.frameToolCallId;
@@ -2632,19 +2552,14 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2632
2552
  }
2633
2553
  }
2634
2554
  });
2635
- if (isTerminalTurnStatus(turn.turn.status)) {
2636
- projectorRef.current.settlement.terminalReceipt = turn.turn;
2637
- state.terminalTurnNotificationQueued = true;
2638
- deadlines.beginSettlement(Date.now());
2639
- }
2640
- emitLifecycleStart();
2641
2555
  const activeProjector = projectorRef.current;
2642
- noteProgress("turn:start");
2556
+ let activationFailed = false;
2643
2557
  const abortListener = () => {
2644
2558
  state.abortCleanup = interruptTurn(activeTurnId).then(async (confirmed) => {
2645
- if (!terminalState.explicitCancellationObserved && !state.permissionChangeRestart && !state.timeout) return;
2559
+ if (!activationFailed && !terminalState.explicitCancellationObserved && !state.permissionChangeRestart && !state.timeout) return;
2646
2560
  if (!confirmed) throw new Error(state.permissionChangeRestart ? "Permission change could not confirm the previous Codex turn stopped." : "Codex cancellation could not confirm the turn stopped; background terminals may still be running.");
2647
- await terminateCodexBackgroundTerminals(resourceState.client, resourceState.thread.threadId);
2561
+ if (resources.nativeProcessAuthority) await resources.nativeProcessAuthority.cancelTurn(resourceState.client, resourceState.thread.threadId, activeTurnId);
2562
+ else await terminateCodexBackgroundTerminals(resourceState.client, resourceState.thread.threadId);
2648
2563
  if (state.permissionChangeRestart) state.permissionChangeRestart = "confirmed";
2649
2564
  });
2650
2565
  state.abortCleanup.then(completeTurn, (error) => {
@@ -2652,32 +2567,18 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2652
2567
  completeTurn();
2653
2568
  });
2654
2569
  };
2655
- runAbortController.signal.addEventListener("abort", abortListener, { once: true });
2656
- if (runAbortController.signal.aborted) abortListener();
2657
- for (const failure of pendingNativePreToolUseFailures.splice(0)) activeProjector.recordNativeToolPreToolUseFailure(failure);
2658
2570
  const notifyUserMessagePersisted = createCodexAppServerUserMessagePersistenceNotifier(params);
2659
- const promptMirrorPromise = mirrorPromptAtTurnStartBestEffort({
2660
- params,
2661
- agentId: sessionAgentId,
2662
- notifyUserMessagePersisted,
2663
- sessionKey: contextSessionKey,
2664
- cwd: effectiveCwd,
2665
- threadId: resourceState.thread.threadId,
2666
- turnId: activeTurnId,
2667
- upstreamUserText: turnState.codexTurnPromptText
2668
- });
2669
- const bindProjection = async () => {
2571
+ const bindProjection = async (promptMirrorPromise) => {
2670
2572
  state.activeLocalProjections += 1;
2671
2573
  try {
2672
- await Promise.race([promptMirrorPromise, completion]);
2673
- if (state.completed) return;
2674
2574
  if (resourceState.turnRoute) try {
2675
- await Promise.race([resourceState.turnRoute.bindTurn(activeTurnId), completion]);
2575
+ await Promise.race([resourceState.turnRoute.bindTurn(activeTurnId, { beforeNotifications: promptMirrorPromise }), completion]);
2676
2576
  } catch (error) {
2677
2577
  if (!state.terminalTurnNotificationQueued && !runAbortController.signal.aborted) throw error;
2678
2578
  await Promise.race([resourceState.turnRoute.drain(), completion]);
2679
2579
  if (!state.completed) throw error;
2680
2580
  }
2581
+ else await Promise.race([promptMirrorPromise, completion]);
2681
2582
  if (!state.completed && isTerminalTurnStatus(turn.turn.status)) {
2682
2583
  if (!isJsonObject(turn.turn)) throw new Error("Codex turn completion payload is not a JSON object");
2683
2584
  await enqueueNotification({
@@ -2707,6 +2608,7 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2707
2608
  });
2708
2609
  const imageContext = {
2709
2610
  workspaceDir: connection.effectiveWorkspace,
2611
+ agentWorkspaceDir: params.workspaceDir,
2710
2612
  model: params.model,
2711
2613
  config: params.config,
2712
2614
  workspaceOnly,
@@ -2723,7 +2625,18 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2723
2625
  requestTimeoutMs: connection.appServer.requestTimeoutMs,
2724
2626
  signal: runAbortController.signal,
2725
2627
  assertActive: assertSteeringActive,
2726
- prepareMessage: async (text, options) => {
2628
+ prepareMessage: async (text, options, assertMessageCurrent) => {
2629
+ const attachmentNote = await prepareAgentWorkspaceAttachments({
2630
+ workspaceDir: params.workspaceDir,
2631
+ turn: {
2632
+ config: params.config,
2633
+ media: options.media,
2634
+ timeoutMs: params.timeoutMs,
2635
+ abortSignal: runAbortController.signal,
2636
+ userTurnTranscriptRecorder: options.userTurnTranscriptRecorder
2637
+ },
2638
+ assertCurrent: assertMessageCurrent
2639
+ });
2727
2640
  const result = await detectAndLoadAgentHarnessPromptImages({
2728
2641
  ...imageContext,
2729
2642
  prompt: text,
@@ -2733,12 +2646,33 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2733
2646
  userTurnTranscriptRecorder: options.userTurnTranscriptRecorder
2734
2647
  });
2735
2648
  if (result.failedMediaCount) throw new Error(`failed to hydrate ${result.failedMediaCount} structured image attachment(s) for Codex steering`);
2736
- return buildCodexUserInput(text, result.images);
2649
+ const source = await buildResolvedCodexUserPromptMessage({
2650
+ ...params,
2651
+ prompt: text,
2652
+ transcriptPrompt: text,
2653
+ userTurnTranscriptRecorder: options.userTurnTranscriptRecorder
2654
+ });
2655
+ return {
2656
+ input: buildCodexUserInput(attachmentNote ? `${text}\n\n${attachmentNote}` : text, result.images),
2657
+ message: {
2658
+ ...source,
2659
+ role: "user",
2660
+ content: result.images.length ? [{
2661
+ type: "text",
2662
+ text
2663
+ }, ...result.images] : text,
2664
+ ...result.images.length ? { __openclaw: {
2665
+ ...asOptionalRecord(Reflect.get(source, "__openclaw")),
2666
+ mediaImageBlockFactIndexes: result.imageFactIndexes
2667
+ } } : {}
2668
+ }
2669
+ };
2737
2670
  },
2738
2671
  beforeSubmit: async (items) => {
2739
2672
  const transcriptItems = items.filter((item) => item.isInboundUserMessage === true || item.userTurnTranscriptRecorder !== void 0);
2740
2673
  if (transcriptItems.length === 0) return;
2741
- await promptMirrorPromise;
2674
+ await projectionReady;
2675
+ await notifications.drainNotificationQueue();
2742
2676
  assertSteeringActive();
2743
2677
  const messages = activeProjector.buildSteeringTranscriptPrefix();
2744
2678
  if (params.sessionTarget && messages.length > 0) {
@@ -2862,33 +2796,83 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2862
2796
  cancel: () => abortExplicitly("cancelled"),
2863
2797
  abort: () => abortExplicitly("aborted")
2864
2798
  };
2865
- params.replyOperation?.attachBackend(handle);
2866
- setActiveEmbeddedRun(params.sessionId, handle, params.sessionKey, params.sessionFile, sessionAgentId);
2867
2799
  const freezeRunTerminalOutcome = () => {
2868
2800
  if (terminalState.terminalOutcomeFrozen) return;
2869
2801
  terminalState.terminalOutcomeFrozen = true;
2870
2802
  params.abortSignal?.removeEventListener("abort", abortFromUpstream);
2871
2803
  };
2872
- if (!runAbortController.signal.aborted && params.permissionChange && !params.permissionChange.applied()) {
2873
- state.permissionChangeRestart = "requested";
2874
- runAbortController.abort("permission-change");
2875
- }
2804
+ const projectionReady = Promise.resolve().then(async () => {
2805
+ runAbortController.signal.addEventListener("abort", abortListener, { once: true });
2806
+ if (runAbortController.signal.aborted) abortListener();
2807
+ try {
2808
+ emitExecutionPhaseOnce("turn_accepted", { phase: "turn_accepted" });
2809
+ userInputBridgeRef.current = createCodexUserInputBridge({
2810
+ paramsForRun: params,
2811
+ onOrdinaryResponse: (response) => activeProjector.recordUserInputResponse(response),
2812
+ threadId: resourceState.thread.threadId,
2813
+ turnId: activeTurnId,
2814
+ signal: runAbortController.signal
2815
+ });
2816
+ trajectoryRecorder?.recordEvent("prompt.submitted", {
2817
+ threadId: resourceState.thread.threadId,
2818
+ turnId: activeTurnId,
2819
+ prompt: turnState.codexTurnPromptText,
2820
+ imagesCount: params.images?.length ?? 0
2821
+ });
2822
+ if (isTerminalTurnStatus(turn.turn.status)) {
2823
+ activeProjector.settlement.terminalReceipt = turn.turn;
2824
+ state.terminalTurnNotificationQueued = true;
2825
+ deadlines.beginSettlement(Date.now());
2826
+ }
2827
+ emitLifecycleStart({
2828
+ provider: projectionParams.provider,
2829
+ model: projectionParams.modelId
2830
+ });
2831
+ noteProgress("turn:start");
2832
+ for (const failure of pendingNativePreToolUseFailures.splice(0)) activeProjector.recordNativeToolPreToolUseFailure(failure);
2833
+ if (thread.preserveNativeModel !== true && thread.connectionScope !== "supervision" && !params.expectedSessionRuntimeOwnership) {
2834
+ const route = resourceState.turnRoute;
2835
+ params.registerPluginRuntimeRefreshConsumer?.(() => resourceState.turnRoute === route && route?.signal.aborted === false && turnRuntime.turnIdRef.current === activeTurnId && !state.completed && !state.terminalTurnNotificationQueued && !runAbortController.signal.aborted);
2836
+ }
2837
+ params.replyOperation?.attachBackend(handle);
2838
+ setActiveEmbeddedRun(params.sessionId, handle, params.sessionKey, params.sessionFile, sessionAgentId);
2839
+ if (!runAbortController.signal.aborted && params.permissionChange && !params.permissionChange.applied()) {
2840
+ state.permissionChangeRestart = "requested";
2841
+ runAbortController.abort("permission-change");
2842
+ }
2843
+ await bindProjection(mirrorPromptAtTurnStartBestEffort({
2844
+ params,
2845
+ agentId: sessionAgentId,
2846
+ notifyUserMessagePersisted,
2847
+ sessionKey: contextSessionKey,
2848
+ cwd: effectiveCwd,
2849
+ threadId: resourceState.thread.threadId,
2850
+ turnId: activeTurnId,
2851
+ upstreamUserText
2852
+ }));
2853
+ } catch (error) {
2854
+ activationFailed = true;
2855
+ runAbortController.abort(error);
2856
+ throw error;
2857
+ }
2858
+ });
2876
2859
  return {
2877
2860
  activeTurnId,
2878
2861
  activeProjector,
2879
2862
  runtimeModelSelection,
2880
2863
  streamState,
2864
+ prepareReplyMedia,
2881
2865
  handle,
2882
2866
  freezeRunTerminalOutcome,
2883
2867
  notifyUserMessagePersisted,
2884
2868
  abortListener,
2885
- ready: bindProjection()
2869
+ ready: projectionReady
2886
2870
  };
2887
2871
  }
2888
2872
  //#endregion
2889
2873
  //#region extensions/codex/src/app-server/run-attempt-cleanup.ts
2890
2874
  async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRuntime, activeTurn) {
2891
- const { prompt, state: resourceState, trajectoryRecorder, releaseCurrentRoute, releaseSharedClientLeaseAndRetireOneShotClient, releaseSandboxExecEnvironment, runCleanupStep } = resources;
2875
+ const { prompt, state: resourceState, trajectoryRecorder, releaseCurrentRoute, releaseSharedClientLeaseAndRetireOneShotClient, releaseSandboxExecEnvironment, releaseNativeProcessAuthority, retainThreadSubscription, releaseThreadSubscription, runCleanupStep } = resources;
2892
2876
  const { connection } = prompt.context.runtime;
2893
2877
  const { params, options, runAbortController, terminalState, bindingStore, bindingIdentity } = connection;
2894
2878
  const { state, steeringQueueRef, userInputBridgeRef, deadlines } = turnRuntime;
@@ -2906,6 +2890,7 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2906
2890
  else await state.abortCleanup?.catch(() => {});
2907
2891
  if (params.oneShotCliRun) await runCleanupStep("codex-one-shot-terminals", () => terminateCodexBackgroundTerminals(resourceState.client, resourceState.thread.threadId, true));
2908
2892
  try {
2893
+ await state.pluginRuntimeRefreshStop;
2909
2894
  steeringQueueRef.current?.cancel();
2910
2895
  if (params.isFinalFallbackAttempt !== false) await maybeEmitFastModeAutoResetBestEffort();
2911
2896
  codexModelCallDiagnostics.emitError("codex app-server run completed without model-call terminal event");
@@ -2925,56 +2910,60 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2925
2910
  aborted: runAbortController.signal.aborted && !state.clientClosedAbort
2926
2911
  });
2927
2912
  await runCleanupStep("codex-trajectory-flush", () => trajectoryRecorder?.flush());
2928
- const retainLiveIncognitoThread = (terminalState.settledTurnStatus === "completed" || state.permissionChangeRestart === "confirmed" && !params.abortSignal?.aborted) && isIncognitoSessionKey(params.sessionKey);
2929
- const retainLiveThread = ((retainLiveIncognitoThread && resourceState.thread.liveThreadEphemeralPolicy !== void 0 || terminalState.settledTurnStatus !== void 0 && !isIncognitoSessionKey(params.sessionKey) && params.cleanupBundleMcpOnRunEnd !== true && resourceState.thread.liveThreadConfigFingerprint !== void 0 && resourceState.thread.preserveNativeModel !== true && resourceState.thread.connectionScope !== "supervision" && !resourceState.thread.ringZeroConfigFingerprint) && resourceState.thread.clientId === resolveCodexAppServerClientInstanceId(resourceState.client) ? bindingStore.read(bindingIdentity)?.threadId === resourceState.thread.threadId && await bindingStore.withLease(bindingIdentity, async () => {
2930
- if (bindingStore.read(bindingIdentity)?.threadId !== resourceState.thread.threadId) return false;
2931
- return await retainCodexAppServerBindingSubscription(resourceState.client, resourceState.thread.threadId, {
2932
- release: resourceState.thread.liveThreadOwnership?.release,
2933
- configFingerprint: resourceState.thread.liveThreadConfigFingerprint,
2934
- serviceTier: connection.mutable.pluginAppServer.serviceTier,
2935
- ephemeralPolicy: resourceState.thread.liveThreadEphemeralPolicy
2936
- });
2937
- }) : false) || retainLiveIncognitoThread && resourceState.thread.liveThreadEphemeralPolicy === void 0;
2938
- const bindingReleased = isIncognitoSessionKey(params.sessionKey) && !retainLiveThread ? await bindingStore.mutate(bindingIdentity, {
2939
- kind: "clear",
2940
- threadId: resourceState.thread.threadId
2941
- }) : true;
2942
- if (!retainLiveThread) {
2943
- if (bindingReleased) {
2944
- if (!await unsubscribeCodexThreadBestEffort(resourceState.client, {
2945
- threadId: resourceState.thread.threadId,
2946
- timeoutMs: 5e3
2947
- })) {
2948
- await closeCodexStartupClientBestEffort(resourceState.client);
2949
- if (params.oneShotCliRun) await runCleanupStep("codex-one-shot-unsubscribe", async () => {
2950
- throw new Error("Codex one-shot thread unsubscribe was not confirmed");
2951
- });
2913
+ const pluginRuntimeRefreshing = state.pluginRuntimeRefreshStop !== void 0 && !runAbortController.signal.aborted && terminalState.settledTurnStatus === "completed";
2914
+ const retainLiveIncognitoThread = !pluginRuntimeRefreshing && (terminalState.settledTurnStatus === "completed" || state.permissionChangeRestart === "confirmed" && !params.abortSignal?.aborted) && isIncognitoSessionKey(params.sessionKey);
2915
+ const retainLiveThread = !pluginRuntimeRefreshing && (retainLiveIncognitoThread && resourceState.thread.liveThreadEphemeralPolicy !== void 0 || terminalState.settledTurnStatus !== void 0 && !isIncognitoSessionKey(params.sessionKey) && params.cleanupBundleMcpOnRunEnd !== true && resourceState.thread.liveThreadConfigFingerprint !== void 0 && !resourceState.thread.ringZeroConfigFingerprint) && await retainThreadSubscription() || retainLiveIncognitoThread && resourceState.thread.liveThreadEphemeralPolicy === void 0;
2916
+ if (pluginRuntimeRefreshing) await withExclusiveCodexAppServerThread({
2917
+ bindingStore,
2918
+ identity: bindingIdentity,
2919
+ threadId: resourceState.thread.threadId,
2920
+ run: () => bindingStore.withLease(bindingIdentity, async () => {
2921
+ if (!isSameCodexAppServerThreadOwner(bindingStore.read(bindingIdentity), resourceState.thread)) throw new Error("Codex plugin refresh lost its managed thread binding.");
2922
+ if (!await releaseThreadSubscription(connection.assertCurrent)) throw new Error("Plugin reload could not release the previous Codex thread.");
2923
+ if (!await bindingStore.mutate(bindingIdentity, {
2924
+ kind: "clear",
2925
+ threadId: resourceState.thread.threadId
2926
+ }, connection.assertCurrent)) throw new Error("Codex plugin refresh lost its managed thread binding.");
2927
+ })
2928
+ });
2929
+ else {
2930
+ const bindingReleased = isIncognitoSessionKey(params.sessionKey) && !retainLiveThread ? await bindingStore.mutate(bindingIdentity, {
2931
+ kind: "clear",
2932
+ threadId: resourceState.thread.threadId
2933
+ }) : true;
2934
+ if (!retainLiveThread) {
2935
+ if (bindingReleased) {
2936
+ if (!await releaseThreadSubscription()) {
2937
+ if (params.oneShotCliRun) await runCleanupStep("codex-one-shot-unsubscribe", async () => {
2938
+ throw new Error("Codex one-shot thread unsubscribe was not confirmed");
2939
+ });
2940
+ }
2952
2941
  }
2953
2942
  }
2954
2943
  }
2955
2944
  } finally {
2945
+ if (resourceState.thread.liveThreadOwnership) await runCleanupStep("codex-unsettled-subscription", async () => {
2946
+ await releaseThreadSubscription();
2947
+ });
2956
2948
  await runCleanupStep("codex-user-input-cancel", () => userInputBridgeRef.current?.cancelPending());
2957
2949
  await runCleanupStep("codex-turn-deadline-clear", () => deadlines.dispose());
2958
- await runCleanupStep("codex-dynamic-tool-cleanup", async () => {
2959
- const cleanupReason = terminalState.settledTurnStatus === "completed" ? "completion" : state.timeout ? "timeout" : runAbortController.signal.aborted ? "cancel" : "error";
2960
- const cleanups = prompt.context.attemptTools.runCleanups.splice(0);
2961
- const errors = (await Promise.allSettled(cleanups.map(async (cleanup) => await cleanup(cleanupReason)))).filter((result) => result.status === "rejected");
2962
- if (params.oneShotCliRun && errors.length) throw new AggregateError(errors.map((result) => result.reason), "Codex tool cleanup failed");
2963
- });
2950
+ await prompt.context.attemptTools.disposeTools(terminalState.settledTurnStatus === "completed" ? "completion" : state.timeout ? "timeout" : runAbortController.signal.aborted ? "cancel" : "error");
2964
2951
  await runCleanupStep("codex-route-release", releaseCurrentRoute);
2965
2952
  await checkpointCleanup;
2966
2953
  await runCleanupStep("codex-shared-client-release", releaseSharedClientLeaseAndRetireOneShotClient);
2967
2954
  const nativeHookRelay = resourceState.nativeHookRelay;
2968
2955
  resourceState.nativeHookRelay = void 0;
2969
- await runCleanupStep("codex-native-hook-relay-release", () => {
2956
+ await runCleanupStep("codex-native-hook-relay-release", async () => {
2970
2957
  if (!nativeHookRelay) return;
2971
2958
  if (state.shouldDelayNativeHookRelayUnregister && !params.oneShotCliRun) scheduleCodexNativeHookRelayUnregister({
2972
2959
  relay: nativeHookRelay,
2973
2960
  hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec
2974
2961
  });
2975
2962
  else nativeHookRelay.unregister();
2963
+ await nativeHookRelay.drain();
2976
2964
  });
2977
2965
  await runCleanupStep("codex-sandbox-release", releaseSandboxExecEnvironment);
2966
+ await runCleanupStep("codex-native-process-source-release", releaseNativeProcessAuthority);
2978
2967
  await runCleanupStep("codex-abort-listener-remove", () => {
2979
2968
  runAbortController.signal.removeEventListener("abort", abortListener);
2980
2969
  });
@@ -2987,70 +2976,37 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2987
2976
  await state.abortCleanup;
2988
2977
  }
2989
2978
  //#endregion
2990
- //#region extensions/codex/src/app-server/workspace-dir-cache.ts
2991
- /** Process-local cache of Codex workspaces already created by the run loop. */
2992
- const codexWorkspaceDirCache = /* @__PURE__ */ new Set();
2993
- //#endregion
2994
- //#region extensions/codex/src/app-server/run-attempt-lifecycle.ts
2995
- const CODEX_APP_SERVER_PROJECTED_CHARS_PER_TOKEN = 4;
2996
- function shouldKeepCodexSharedAbortOpen(params) {
2997
- const terminal = attemptTerminal.project(params.result.terminal);
2998
- if (params.explicitCancellationObserved || terminal.aborted || terminal.externalAbort) return false;
2999
- return params.trigger === "memory" || !params.attemptSucceeded;
3000
- }
3001
- function withCodexAppServerFastModeServiceTier(appServer, params) {
3002
- const fastMode = typeof params.fastMode === "function" ? params.fastMode() : params.fastMode;
3003
- const serviceTier = fastMode === void 0 ? appServer.serviceTier : fastMode ? "priority" : void 0;
3004
- if (serviceTier === appServer.serviceTier) return appServer;
3005
- if (serviceTier) return {
3006
- ...appServer,
3007
- serviceTier
3008
- };
2979
+ //#region extensions/codex/src/app-server/session-binding-scope.ts
2980
+ /** Carries one prepared run identity through callers that rederive it from public params. */
2981
+ function scopeCodexRunBindingStore(params) {
2982
+ const mapSessionIdentity = (identity) => identity.agentId === params.logicalIdentity.agentId && identity.sessionId === params.logicalIdentity.sessionId && identity.sessionKey?.trim() === params.logicalIdentity.sessionKey?.trim() ? params.physicalIdentity : identity;
2983
+ const mapIdentity = (identity) => identity.kind === "session" ? mapSessionIdentity(identity) : identity;
2984
+ const mapHistoryOwner = (identity, owner) => {
2985
+ const mapped = mapIdentity(identity);
2986
+ return identity.kind === "session" && mapped.kind === "session" && owner.sessionId === identity.sessionId ? {
2987
+ ...owner,
2988
+ sessionId: mapped.sessionId
2989
+ } : owner;
2990
+ };
2991
+ const readMany = params.bindingStore.readMany?.bind(params.bindingStore);
3009
2992
  return {
3010
- ...appServer,
3011
- serviceTier: null
3012
- };
3013
- }
3014
- function estimateCodexAppServerProjectedTurnTokens(params) {
3015
- const inputChars = params.prompt.length + (params.developerInstructions?.length ?? 0);
3016
- return Math.max(1, Math.ceil(inputChars / CODEX_APP_SERVER_PROJECTED_CHARS_PER_TOKEN));
3017
- }
3018
- async function ensureCodexWorkspaceDirOnce(workspaceDir) {
3019
- const normalized = path.resolve(workspaceDir);
3020
- if (codexWorkspaceDirCache.has(normalized)) return;
3021
- await fs.mkdir(normalized, { recursive: true });
3022
- codexWorkspaceDirCache.add(normalized);
3023
- }
3024
- async function emitCodexAppServerEvent(params, event) {
3025
- try {
3026
- emitAgentEvent({
3027
- runId: params.runId,
3028
- stream: event.stream,
3029
- data: event.data,
3030
- ...params.sessionKey ? { sessionKey: params.sessionKey } : {}
3031
- });
3032
- } catch (error) {
3033
- embeddedAgentLog.debug("codex app-server global agent event emit failed", { error });
3034
- }
3035
- try {
3036
- await params.onAgentEvent?.(event);
3037
- } catch (error) {
3038
- embeddedAgentLog.debug("codex app-server agent event handler threw", { error });
3039
- }
3040
- }
3041
- async function runCodexAgentEndHook(params, hookParams) {
3042
- const sideEffectParams = {
3043
- ...hookParams,
3044
- ctx: {
3045
- ...hookParams.ctx,
3046
- config: params.config
3047
- }
2993
+ ...params.bindingStore,
2994
+ read: (identity) => params.bindingStore.read(mapIdentity(identity)),
2995
+ ...readMany ? { readMany: (identities) => readMany(identities.map(mapIdentity)) } : {},
2996
+ readNativeSubagentSubmissions: (identity, owner) => params.bindingStore.readNativeSubagentSubmissions(mapIdentity(identity), mapHistoryOwner(identity, owner)),
2997
+ hasOtherThreadOwner: (threadId, identity) => params.bindingStore.hasOtherThreadOwner(threadId, identity ? mapIdentity(identity) : void 0),
2998
+ mutate: (identity, mutation, assertCurrent) => params.bindingStore.mutate(mapIdentity(identity), mutation.kind === "record-native-subagent-submission" || mutation.kind === "consume-native-subagent-submission" ? {
2999
+ ...mutation,
3000
+ owner: mapHistoryOwner(identity, mutation.owner)
3001
+ } : mutation, assertCurrent),
3002
+ prepareSessionGenerationReclaim: (identity) => params.bindingStore.prepareSessionGenerationReclaim(mapSessionIdentity(identity)),
3003
+ adoptSessionGeneration: (identity, expectedPreviousSessionId, assertCurrent) => params.bindingStore.adoptSessionGeneration(mapSessionIdentity(identity), expectedPreviousSessionId, assertCurrent),
3004
+ resetSessionGeneration: (identity) => params.bindingStore.resetSessionGeneration(mapSessionIdentity(identity)),
3005
+ retireSessionGeneration: (identity) => params.bindingStore.retireSessionGeneration(mapSessionIdentity(identity)),
3006
+ withSessionDeletion: (identity, assertCurrent, run) => params.bindingStore.withSessionDeletion(mapSessionIdentity(identity), assertCurrent, run),
3007
+ withThreadArchiveFence: (run) => params.bindingStore.withThreadArchiveFence(run),
3008
+ withLease: (identity, run) => params.bindingStore.withLease(mapIdentity(identity), run)
3048
3009
  };
3049
- if (!params.messageChannel && !params.messageProvider) {
3050
- await awaitAgentEndSideEffects(sideEffectParams);
3051
- return;
3052
- }
3053
- runAgentEndSideEffects(sideEffectParams);
3054
3010
  }
3055
3011
  //#endregion
3056
3012
  //#region extensions/codex/src/app-server/startup-binding.ts
@@ -3074,7 +3030,6 @@ const CODEX_APP_SERVER_BYTE_UNITS = {
3074
3030
  tb: 1099511627776,
3075
3031
  tib: 1099511627776
3076
3032
  };
3077
- const codexSessionRecordCache = /* @__PURE__ */ new Map();
3078
3033
  function parseCodexAppServerByteLimit(value) {
3079
3034
  if (typeof value === "number" && Number.isFinite(value) && value > 0) return Math.floor(value);
3080
3035
  if (typeof value !== "string") return;
@@ -3142,51 +3097,6 @@ async function listCodexAppServerRolloutFilesForThread(agentDir, threadId, codex
3142
3097
  }
3143
3098
  return files;
3144
3099
  }
3145
- async function readCodexSessionRecordForSessionFile(sessionFile) {
3146
- if (isSqliteSessionFileMarker(sessionFile)) return;
3147
- const sessionsFile = path.join(path.dirname(sessionFile), "sessions.json");
3148
- const resolvedSessionFile = path.resolve(sessionFile);
3149
- let stat;
3150
- try {
3151
- stat = await fs.stat(sessionsFile);
3152
- } catch {
3153
- codexSessionRecordCache.delete(resolvedSessionFile);
3154
- return;
3155
- }
3156
- const cached = codexSessionRecordCache.get(resolvedSessionFile);
3157
- if (cached?.sessionsFile === sessionsFile && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) return cached.record;
3158
- let store;
3159
- try {
3160
- store = JSON.parse(await fs.readFile(sessionsFile, "utf8"));
3161
- } catch {
3162
- codexSessionRecordCache.delete(resolvedSessionFile);
3163
- return;
3164
- }
3165
- if (!isJsonObject(store)) {
3166
- codexSessionRecordCache.delete(resolvedSessionFile);
3167
- return;
3168
- }
3169
- let found;
3170
- for (const [sessionKey, record] of Object.entries(store)) {
3171
- if (!isJsonObject(record) || typeof record.sessionFile !== "string") continue;
3172
- if (path.resolve(record.sessionFile) !== resolvedSessionFile) continue;
3173
- found = {
3174
- sessionKey,
3175
- ...record
3176
- };
3177
- break;
3178
- }
3179
- codexSessionRecordCache.set(resolvedSessionFile, {
3180
- sessionsFile,
3181
- mtimeMs: stat.mtimeMs,
3182
- size: stat.size,
3183
- record: found
3184
- });
3185
- return found;
3186
- }
3187
- function isSqliteSessionFileMarker(sessionFile) {
3188
- return parseSqliteSessionFileMarker(sessionFile) !== void 0;
3189
- }
3190
3100
  async function readCodexAppServerRolloutTokenSnapshot(file, openedHandle) {
3191
3101
  let handle = openedHandle;
3192
3102
  if (!handle) try {
@@ -3210,12 +3120,8 @@ async function readCodexAppServerRolloutTokenSnapshot(file, openedHandle) {
3210
3120
  const bytesToRead = Math.min(position, CODEX_APP_SERVER_ROLLOUT_TAIL_READ_BYTES);
3211
3121
  const nextPosition = position - bytesToRead;
3212
3122
  const chunk = Buffer.allocUnsafe(bytesToRead);
3213
- let bytesRead = 0;
3214
- while (bytesRead < bytesToRead) {
3215
- const result = await handle.read(chunk, bytesRead, bytesToRead - bytesRead, nextPosition + bytesRead);
3216
- if (result.bytesRead === 0) return snapshot;
3217
- bytesRead += result.bytesRead;
3218
- }
3123
+ const bytesRead = await readFileWindowFully(handle, chunk, nextPosition);
3124
+ if (bytesRead < bytesToRead) return snapshot;
3219
3125
  let lineEnd = bytesRead;
3220
3126
  while (lineEnd > 0) {
3221
3127
  const index = chunk.lastIndexOf(10, lineEnd - 1);
@@ -3258,9 +3164,6 @@ function readCodexAppServerRolloutTokenSnapshotLine(line) {
3258
3164
  function readCompactionConfig(config) {
3259
3165
  return isJsonObject(config?.agents?.defaults?.compaction) ? config.agents.defaults.compaction : void 0;
3260
3166
  }
3261
- function resolveCodexAppServerNativeThreadReserveTokens(_config) {
3262
- return CODEX_APP_SERVER_NATIVE_THREAD_DEFAULT_RESERVE_TOKENS;
3263
- }
3264
3167
  function resolveCodexAppServerNativeThreadTokenFuse(params) {
3265
3168
  const projectedTurnTokens = typeof params.projectedTurnTokens === "number" && Number.isFinite(params.projectedTurnTokens) && params.projectedTurnTokens > 0 ? Math.floor(params.projectedTurnTokens) : 0;
3266
3169
  const contextWindow = params.modelContextWindow ?? CODEX_APP_SERVER_NATIVE_THREAD_FALLBACK_MAX_TOKENS;
@@ -3273,11 +3176,6 @@ function maxFiniteNumber(values) {
3273
3176
  if (nums.length === 0) return;
3274
3177
  return Math.max(...nums);
3275
3178
  }
3276
- function minFiniteNumber(values) {
3277
- const nums = values.filter((value) => typeof value === "number" && Number.isFinite(value));
3278
- if (nums.length === 0) return;
3279
- return Math.min(...nums);
3280
- }
3281
3179
  function hasContextEngineThreadBootstrapProjection(binding) {
3282
3180
  return binding.contextEngine?.projection?.mode === "thread_bootstrap";
3283
3181
  }
@@ -3286,7 +3184,6 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
3286
3184
  const binding = params.binding;
3287
3185
  if (!binding?.threadId) return { binding };
3288
3186
  if (binding.connectionScope === "supervision") return { binding };
3289
- const sessionRecord = await readCodexSessionRecordForSessionFile(params.sessionFile);
3290
3187
  const rolloutFiles = await listCodexAppServerRolloutFilesForThread(params.agentDir, binding.threadId, params.codexHome, binding.rolloutPath);
3291
3188
  const maxBytes = parseCodexAppServerByteLimit(readCompactionConfig(params.config)?.maxActiveTranscriptBytes);
3292
3189
  if (maxBytes !== void 0 && params.contextEngineActive === true && hasContextEngineThreadBootstrapProjection(binding)) embeddedAgentLog.debug("codex app-server deferring native transcript byte guard for context-engine thread bootstrap", {
@@ -3320,25 +3217,19 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
3320
3217
  const nativeTokenSnapshots = await Promise.all(rolloutFiles.map(async (file) => readCodexAppServerRolloutTokenSnapshot(file.path, file.handle)));
3321
3218
  const nativeTokens = maxFiniteNumber(nativeTokenSnapshots.map((snapshot) => snapshot?.totalTokens));
3322
3219
  const nativeModelContextWindow = maxFiniteNumber(nativeTokenSnapshots.map((snapshot) => snapshot?.modelContextWindow));
3323
- const sessionModelContextWindow = typeof sessionRecord?.contextTokens === "number" && Number.isFinite(sessionRecord.contextTokens) && sessionRecord.contextTokens > 0 ? Math.floor(sessionRecord.contextTokens) : void 0;
3324
- const reserveTokens = resolveCodexAppServerNativeThreadReserveTokens(params.config);
3220
+ const reserveTokens = CODEX_APP_SERVER_NATIVE_THREAD_DEFAULT_RESERVE_TOKENS;
3325
3221
  const maxTokens = resolveCodexAppServerNativeThreadTokenFuse({
3326
- modelContextWindow: minFiniteNumber([nativeModelContextWindow, sessionModelContextWindow]),
3222
+ modelContextWindow: nativeModelContextWindow,
3327
3223
  reserveTokens,
3328
3224
  projectedTurnTokens: params.projectedTurnTokens
3329
3225
  });
3330
- const sessionTokens = sessionRecord?.totalTokensFresh === true && sessionRecord.totalTokensVersion === 1 && typeof sessionRecord?.totalTokens === "number" && Number.isFinite(sessionRecord.totalTokens) ? sessionRecord.totalTokens : void 0;
3331
- const tokenCount = maxFiniteNumber([sessionTokens, nativeTokens]);
3332
- if (tokenCount !== void 0 && tokenCount >= maxTokens) {
3226
+ if (nativeTokens !== void 0 && nativeTokens >= maxTokens) {
3333
3227
  assertCodexBindingMayBeReplaced(binding, "rotating a full native context", params.expectedSessionRuntimeOwnership);
3334
3228
  embeddedAgentLog.warn("codex app-server native transcript exceeded active token limit; starting a fresh thread", {
3335
3229
  threadId: binding.threadId,
3336
3230
  maxTokens,
3337
- sessionKey: sessionRecord?.sessionKey,
3338
- sessionTokens,
3339
3231
  nativeTokens,
3340
3232
  nativeModelContextWindow,
3341
- sessionModelContextWindow,
3342
3233
  reserveTokens,
3343
3234
  projectedTurnTokens: params.projectedTurnTokens
3344
3235
  });
@@ -3348,10 +3239,9 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
3348
3239
  }, params.assertCurrent);
3349
3240
  return { binding: void 0 };
3350
3241
  }
3351
- const startupContextTokens = nativeModelContextWindow ?? sessionModelContextWindow;
3352
3242
  return {
3353
3243
  binding,
3354
- ...startupContextTokens ? { startupContextTokens } : {}
3244
+ ...nativeModelContextWindow ? { startupContextTokens: nativeModelContextWindow } : {}
3355
3245
  };
3356
3246
  }
3357
3247
  //#endregion
@@ -3473,7 +3363,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3473
3363
  const usesSupervisionConnection = startupBinding?.connectionScope === "supervision";
3474
3364
  if (usesSupervisionConnection) activeContextEngine = void 0;
3475
3365
  if (usesSupervisionConnection && pluginConfig.supervision?.enabled !== true) throw new Error("Codex supervision is disabled; refusing to open a native user-home supervised session");
3476
- const resolveRuntimeOptionsForBinding = (binding, selection) => resolveCodexBindingAppServerConnection({
3366
+ const resolveRuntimeOptionsForBinding = async (binding, selection) => (await resolveCodexBindingAppServerConnection({
3477
3367
  binding,
3478
3368
  pluginConfig,
3479
3369
  execPolicy,
@@ -3483,8 +3373,9 @@ async function prepareCodexAttemptConnection({ params, options }) {
3483
3373
  agentDir,
3484
3374
  requirementsToml,
3485
3375
  openClawSandboxActive: sandbox?.enabled === true,
3486
- sessionPermissionMode: params.permissionMode
3487
- }).appServer;
3376
+ sessionPermissionMode: params.permissionMode,
3377
+ assertCurrent
3378
+ })).appServer;
3488
3379
  const initialStartupBindingHadInactiveThreadBootstrap = isInactiveThreadBootstrapBinding(startupBinding);
3489
3380
  const appServerHomeScope = resolveCodexAppServerHomeScope({ appServer: pluginConfig.appServer });
3490
3381
  const preparedAuthRoute = usesSupervisionConnection ? void 0 : params.runtimePlan?.auth.modelRoute;
@@ -3527,7 +3418,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3527
3418
  };
3528
3419
  let reviewerPolicyContext = resolveReviewerPolicyContext(startupBinding);
3529
3420
  preDynamicStartupStages.mark("auth-profile");
3530
- let configuredAppServer = resolveRuntimeOptionsForBinding(startupBinding, {
3421
+ let configuredAppServer = await resolveRuntimeOptionsForBinding(startupBinding, {
3531
3422
  modelProvider: reviewerPolicyContext.modelProvider,
3532
3423
  model: reviewerPolicyContext.model
3533
3424
  });
@@ -3626,9 +3517,8 @@ async function prepareCodexAttemptConnection({ params, options }) {
3626
3517
  binding: startupBinding,
3627
3518
  bindingStore,
3628
3519
  identity: bindingIdentity,
3629
- sessionFile: params.sessionFile,
3630
3520
  agentDir,
3631
- codexHome: appServer.start.env?.CODEX_HOME,
3521
+ codexHome: appServer.start.codexHome ?? appServer.start.env?.CODEX_HOME,
3632
3522
  config: params.config,
3633
3523
  contextEngineActive: Boolean(activeContextEngine),
3634
3524
  expectedSessionRuntimeOwnership: params.expectedSessionRuntimeOwnership
@@ -3638,7 +3528,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3638
3528
  preDynamicStartupStages.mark("rotate-binding");
3639
3529
  if (startupBinding !== startupBindingBeforeRotation) {
3640
3530
  reviewerPolicyContext = resolveReviewerPolicyContext(startupBinding);
3641
- configuredAppServer = resolveRuntimeOptionsForBinding(startupBinding, {
3531
+ configuredAppServer = await resolveRuntimeOptionsForBinding(startupBinding, {
3642
3532
  modelProvider: reviewerPolicyContext.modelProvider,
3643
3533
  model: reviewerPolicyContext.model
3644
3534
  });
@@ -3666,7 +3556,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3666
3556
  pluginAppServer: appServer,
3667
3557
  continuityCalibration: startupBindingBeforeRotation?.continuityCalibration
3668
3558
  };
3669
- const resolveRuntimeOptionsForCurrentBinding = (selection) => resolveFinalAppServer(resolveRuntimeOptionsForBinding(mutable.startupBinding, selection), selection).appServer;
3559
+ const resolveRuntimeOptionsForCurrentBinding = async (selection) => resolveFinalAppServer(await resolveRuntimeOptionsForBinding(mutable.startupBinding, selection), selection).appServer;
3670
3560
  assertCurrent();
3671
3561
  return {
3672
3562
  params,
@@ -3723,19 +3613,327 @@ async function prepareCodexAttemptConnection({ params, options }) {
3723
3613
  }
3724
3614
  }
3725
3615
  //#endregion
3726
- //#region extensions/codex/src/app-server/run-attempt-state.ts
3727
- async function clearCodexBindingAfterInvalidImagePayload(bindingStore, identity, fields, expected) {
3728
- const currentBinding = bindingStore.read(identity);
3729
- const expectedThreadId = fields.threadId ?? currentBinding?.threadId;
3730
- if (!expectedThreadId) return;
3731
- if (currentBinding && currentBinding.threadId !== expectedThreadId) {
3732
- embeddedAgentLog.warn("codex app-server image payload error detected for unbound thread; preserving thread binding", {
3733
- ...fields,
3734
- boundThreadId: currentBinding.threadId
3735
- });
3616
+ //#region extensions/codex/src/app-server/attempt-context.ts
3617
+ /**
3618
+ * Builds Codex app-server prompt context,
3619
+ * system-prompt reports, and context-engine projection decisions.
3620
+ */
3621
+ /** Reads mirrored Codex session history for harness hooks. */
3622
+ async function readMirroredSessionHistoryMessages(params) {
3623
+ const { admission, signal, contextTokenBudget, ...target } = params;
3624
+ const messages = await readCodexMirroredSessionHistoryMessages(target, admission, signal, contextTokenBudget);
3625
+ if (!messages) embeddedAgentLog.warn("failed to read mirrored session history for codex harness hooks", { sessionFile: params.sessionFile });
3626
+ return messages;
3627
+ }
3628
+ /** Reads a valid thread-bootstrap projection request from context-engine output. */
3629
+ function readContextEngineThreadBootstrapProjection(projection) {
3630
+ if (projection?.mode !== "thread_bootstrap") return;
3631
+ const epoch = projection.epoch?.trim();
3632
+ if (!epoch) {
3633
+ embeddedAgentLog.warn("context engine requested Codex thread-bootstrap projection without an epoch; using per-turn projection");
3736
3634
  return;
3737
3635
  }
3738
- if (expected || currentBinding?.connectionScope === "supervision") {
3636
+ const fingerprint = projection.fingerprint?.trim();
3637
+ return {
3638
+ mode: "thread_bootstrap",
3639
+ epoch,
3640
+ ...fingerprint ? { fingerprint } : {}
3641
+ };
3642
+ }
3643
+ /**
3644
+ * Decides whether an existing Codex thread can reuse its context-engine
3645
+ * bootstrap projection or must be reprojected.
3646
+ */
3647
+ function resolveContextEngineBootstrapProjectionDecision(params) {
3648
+ const bindingProjection = params.startupBinding?.contextEngine?.projection;
3649
+ if (!params.startupBinding?.threadId || !bindingProjection) return {
3650
+ project: true,
3651
+ reason: !params.startupBinding?.threadId ? "missing-thread-binding" : "missing-projection-binding"
3652
+ };
3653
+ if (!params.expectedBinding || !isContextEngineBindingCompatible(params.startupBinding.contextEngine, params.expectedBinding)) return {
3654
+ project: true,
3655
+ reason: "context-engine-binding-mismatch"
3656
+ };
3657
+ if (!areCodexDynamicToolFingerprintsCompatible({
3658
+ previous: params.startupBinding.dynamicToolsFingerprint,
3659
+ next: params.dynamicToolsFingerprint,
3660
+ nextLegacy: params.legacyDynamicToolsFingerprint
3661
+ })) return {
3662
+ project: true,
3663
+ reason: "dynamic-tools-mismatch"
3664
+ };
3665
+ return bindingProjection.mode !== "thread_bootstrap" || bindingProjection.epoch !== params.projection.epoch || bindingProjection.fingerprint !== params.projection.fingerprint ? {
3666
+ project: true,
3667
+ reason: "projection-mismatch"
3668
+ } : {
3669
+ project: false,
3670
+ reason: "matching-thread-bootstrap-binding"
3671
+ };
3672
+ }
3673
+ /**
3674
+ * Builds the prompt-size, bootstrap-file, skill, and tool-schema accounting
3675
+ * report for a Codex run.
3676
+ */
3677
+ function buildCodexSystemPromptReport(params) {
3678
+ const toolEntries = flattenCodexDynamicToolFunctions(params.tools).map(buildCodexToolReportEntry);
3679
+ const schemaChars = toolEntries.reduce((sum, tool) => sum + tool.schemaChars, 0);
3680
+ const skillsPrompt = params.skillsPrompt.trim();
3681
+ const bootstrapMaxChars = readPositiveNumber(params.attempt.config?.agents?.defaults?.bootstrapMaxChars);
3682
+ const bootstrapTotalMaxChars = readPositiveNumber(params.attempt.config?.agents?.defaults?.bootstrapTotalMaxChars);
3683
+ return {
3684
+ source: "run",
3685
+ generatedAt: Date.now(),
3686
+ sessionId: params.attempt.sessionId,
3687
+ sessionKey: params.sessionKey,
3688
+ provider: params.attempt.provider,
3689
+ model: params.attempt.modelId,
3690
+ workspaceDir: params.workspaceDir,
3691
+ ...bootstrapMaxChars ? { bootstrapMaxChars } : {},
3692
+ ...bootstrapTotalMaxChars ? { bootstrapTotalMaxChars } : {},
3693
+ systemPrompt: {
3694
+ chars: params.developerInstructions.length,
3695
+ projectContextChars: 0,
3696
+ nonProjectContextChars: params.developerInstructions.length,
3697
+ hash: sha256Text(params.developerInstructions)
3698
+ },
3699
+ injectedWorkspaceFiles: buildCodexBootstrapInjectionStats({
3700
+ bootstrapFiles: params.workspaceBootstrapContext.bootstrapFiles,
3701
+ injectedFiles: params.workspaceBootstrapContext.promptContextFiles ?? [],
3702
+ omitReferenceFiles: params.omitWorkspaceReferences,
3703
+ developerInstructionFiles: [...params.workspaceBootstrapContext.threadDeveloperInstructionFiles ?? [], ...params.workspaceBootstrapContext.turnScopedDeveloperInstructionFiles ?? []],
3704
+ memoryToolRoutedBootstrapFiles: params.workspaceBootstrapContext.memoryToolRoutedBootstrapFiles ?? [],
3705
+ memoryToolRouted: params.workspaceBootstrapContext.memoryToolRouted === true
3706
+ }),
3707
+ skills: {
3708
+ promptChars: skillsPrompt.length,
3709
+ hash: sha256Text(skillsPrompt),
3710
+ entries: buildCodexSkillReportEntries(skillsPrompt)
3711
+ },
3712
+ tools: {
3713
+ listChars: 0,
3714
+ schemaChars,
3715
+ entries: toolEntries
3716
+ }
3717
+ };
3718
+ }
3719
+ function buildCodexSkillReportEntries(skillsPrompt) {
3720
+ if (!skillsPrompt) return [];
3721
+ return Array.from(skillsPrompt.matchAll(/<skill>[\s\S]*?<\/skill>/gi)).map((match) => match[0] ?? "").map((block) => ({
3722
+ name: block.match(/<name>\s*([^<]+?)\s*<\/name>/i)?.[1]?.trim() || "(unknown)",
3723
+ blockChars: block.length
3724
+ })).filter((entry) => entry.blockChars > 0);
3725
+ }
3726
+ function buildCodexToolReportEntry(tool) {
3727
+ const summary = tool.description.trim();
3728
+ if (tool.deferLoading === true) return {
3729
+ name: tool.name,
3730
+ summaryChars: summary.length,
3731
+ summaryHash: sha256Text(summary),
3732
+ schemaChars: 0,
3733
+ schemaHash: stableJsonHash(null),
3734
+ propertiesCount: null
3735
+ };
3736
+ return {
3737
+ name: tool.name,
3738
+ summaryChars: summary.length,
3739
+ summaryHash: sha256Text(summary),
3740
+ ...buildCodexToolSchemaStats(tool.inputSchema)
3741
+ };
3742
+ }
3743
+ function buildCodexToolSchemaStats(schema) {
3744
+ const schemaChars = (() => {
3745
+ try {
3746
+ return JSON.stringify(schema).length;
3747
+ } catch {
3748
+ return 0;
3749
+ }
3750
+ })();
3751
+ const properties = isJsonObject(schema) && isJsonObject(schema.properties) ? schema.properties : null;
3752
+ return {
3753
+ schemaChars,
3754
+ schemaHash: stableJsonHash(schema),
3755
+ propertiesCount: properties ? Object.keys(properties).length : null
3756
+ };
3757
+ }
3758
+ function sha256Text(value) {
3759
+ return createHash("sha256").update(value).digest("hex");
3760
+ }
3761
+ function normalizeForStableHash(value) {
3762
+ if (Array.isArray(value)) return value.map((entry) => normalizeForStableHash(entry));
3763
+ if (value && typeof value === "object") {
3764
+ const record = value;
3765
+ return Object.fromEntries(Object.keys(record).toSorted((left, right) => left.localeCompare(right)).map((key) => [key, normalizeForStableHash(record[key])]));
3766
+ }
3767
+ return value;
3768
+ }
3769
+ function stableJsonHash(value) {
3770
+ return sha256Text(JSON.stringify(normalizeForStableHash(value)) ?? "null");
3771
+ }
3772
+ function buildCodexBootstrapInjectionStats(params) {
3773
+ const injectedIndex = indexCodexContextFileContent(params.injectedFiles);
3774
+ const developerInstructionIndex = indexCodexContextFileContent(params.developerInstructionFiles ?? []);
3775
+ const memoryToolRoutedPaths = new Set((params.memoryToolRoutedBootstrapFiles ?? []).map((file) => readNonBlankString(file.path)).filter(isNonEmptyString$1).map(normalizeCodexContextFilePath));
3776
+ return params.bootstrapFiles.map((file) => {
3777
+ const fileName = readNonBlankString(file.name);
3778
+ const pathValue = readNonBlankString(file.path) ?? fileName ?? "";
3779
+ const displayName = (fileName ?? getCodexContextFileDisplayBasename(pathValue)) || pathValue;
3780
+ const baseName = getCodexContextFileBasename(pathValue || fileName || "");
3781
+ const rawChars = file.missing ? 0 : (file.content ?? "").trimEnd().length;
3782
+ const memoryToolRoutedFile = baseName === "memory.md" && params.memoryToolRouted === true && memoryToolRoutedPaths.has(normalizeCodexContextFilePath(pathValue));
3783
+ const injected = memoryToolRoutedFile ? void 0 : readCodexIndexedContextFileContent(injectedIndex, pathValue, fileName) ?? readCodexIndexedContextFileContent(developerInstructionIndex, pathValue, fileName);
3784
+ if (!file.missing && injected === void 0 && CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName)) return {
3785
+ name: displayName,
3786
+ path: pathValue,
3787
+ missing: false,
3788
+ rawChars,
3789
+ injectionStatus: "native_unverified",
3790
+ injectedChars: null,
3791
+ truncated: null
3792
+ };
3793
+ const omitted = memoryToolRoutedFile || params.omitReferenceFiles && readCodexIndexedContextFileContent(injectedIndex, pathValue, fileName) !== void 0;
3794
+ const injectedChars = omitted ? 0 : injected?.length ?? 0;
3795
+ const truncated = omitted ? false : !file.missing && injectedChars < rawChars;
3796
+ return {
3797
+ name: displayName,
3798
+ path: pathValue,
3799
+ missing: file.missing,
3800
+ rawChars,
3801
+ injectedChars,
3802
+ truncated
3803
+ };
3804
+ });
3805
+ }
3806
+ function indexCodexContextFileContent(files) {
3807
+ const byPath = /* @__PURE__ */ new Map();
3808
+ const byBaseName = /* @__PURE__ */ new Map();
3809
+ for (const file of files) {
3810
+ const pathValue = readNonBlankString(file.path);
3811
+ if (!pathValue) continue;
3812
+ if (!byPath.has(pathValue)) byPath.set(pathValue, file.content);
3813
+ const baseName = getCodexContextFileBasename(pathValue);
3814
+ if (baseName && !byBaseName.has(baseName)) byBaseName.set(baseName, file.content);
3815
+ }
3816
+ return {
3817
+ byPath,
3818
+ byBaseName
3819
+ };
3820
+ }
3821
+ function readCodexIndexedContextFileContent(index, pathValue, fileName) {
3822
+ const pathContent = index.byPath.get(pathValue);
3823
+ if (pathContent !== void 0) return pathContent;
3824
+ if (fileName) {
3825
+ const nameContent = index.byPath.get(fileName);
3826
+ if (nameContent !== void 0) return nameContent;
3827
+ }
3828
+ const baseName = getCodexContextFileBasename(fileName ?? pathValue);
3829
+ return baseName ? index.byBaseName.get(baseName) : void 0;
3830
+ }
3831
+ function readPositiveNumber(value) {
3832
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
3833
+ }
3834
+ /**
3835
+ * Builds OpenClaw-provided workspace prompt context for the current Codex turn.
3836
+ */
3837
+ function buildCodexOpenClawPromptContext(params) {
3838
+ if (!shouldInjectCodexOpenClawPromptContext(params.params)) return;
3839
+ const sections = [params.workspacePromptContext?.trim() ? [
3840
+ "## OpenClaw Workspace Context",
3841
+ "",
3842
+ params.workspacePromptContext.trim()
3843
+ ].join("\n") : void 0, params.watchedSessionsContext?.trim() || void 0].filter(isNonEmptyString$1);
3844
+ if (sections.length === 0) return;
3845
+ return [
3846
+ "OpenClaw runtime context for this turn:",
3847
+ "Treat this OpenClaw-provided context as supporting project/user reference for the current request.",
3848
+ "",
3849
+ ...sections
3850
+ ].join("\n");
3851
+ }
3852
+ /**
3853
+ * Renders the watched-sessions block for the Codex per-turn runtime context.
3854
+ * Codex builds its own instruction layers, so the embedded prompt's Watched
3855
+ * Sessions section must be re-surfaced here or Codex-backed main sessions
3856
+ * keep refusing cross-session questions (openclaw#114797).
3857
+ */
3858
+ function buildCodexWatchedSessionsContext(params) {
3859
+ if (!shouldInjectCodexOpenClawPromptContext(params.attempt)) return;
3860
+ return buildWatchedSessionsHarnessContext({
3861
+ config: params.attempt.config,
3862
+ sessionKey: params.sessionKey,
3863
+ sandboxed: params.sandboxed,
3864
+ toolNames: flattenCodexDynamicToolFunctions(params.dynamicTools).map((tool) => normalizeCodexDynamicToolName(tool.name))
3865
+ });
3866
+ }
3867
+ /** Renders loaded OpenClaw skill prompts as Codex collaboration instructions. */
3868
+ function renderCodexSkillsCollaborationInstructions(params) {
3869
+ if (!shouldInjectCodexOpenClawPromptContext(params.attempt)) return;
3870
+ return params.skillsPrompt?.trim() ? [
3871
+ "## OpenClaw Skills",
3872
+ "",
3873
+ params.skillsPrompt.trim()
3874
+ ].join("\n") : void 0;
3875
+ }
3876
+ /**
3877
+ * Prepends OpenClaw context while preserving leading delivery metadata as
3878
+ * routing guidance instead of user request text.
3879
+ */
3880
+ function prependCodexOpenClawPromptContext(prompt, context, options = {}) {
3881
+ const { deliveryHint, prompt: promptWithoutDeliveryHint } = splitLeadingCodexDeliveryHint(prompt);
3882
+ if (!context?.trim() && (!deliveryHint || options.preservePromptWithoutContext)) return prompt;
3883
+ const promptSection = promptWithoutDeliveryHint.startsWith("OpenClaw assembled context for this turn:") ? promptWithoutDeliveryHint : ["Current user request:", promptWithoutDeliveryHint].join("\n");
3884
+ const deliverySection = deliveryHint ? [
3885
+ "OpenClaw delivery metadata:",
3886
+ "This delivery metadata is runtime routing guidance, not the user's request.",
3887
+ deliveryHint
3888
+ ].join("\n") : void 0;
3889
+ return [
3890
+ context?.trim(),
3891
+ deliverySection,
3892
+ promptSection
3893
+ ].filter(Boolean).join("\n\n");
3894
+ }
3895
+ /**
3896
+ * Maps the surviving user-request portion of an input range after delivery
3897
+ * metadata has been relocated before the request.
3898
+ */
3899
+ function resolveCodexDeliveryHintPreservedInputRange(params) {
3900
+ const { prompt, promptInputRange, decoratedPrompt } = params;
3901
+ const { deliveryHint, prompt: promptWithoutDeliveryHint } = splitLeadingCodexDeliveryHint(prompt);
3902
+ if (!deliveryHint || !promptInputRange || promptInputRange.start < 0 || promptInputRange.end < promptInputRange.start || promptInputRange.end > prompt.length || !decoratedPrompt.endsWith(promptWithoutDeliveryHint)) return;
3903
+ const promptWithoutDeliveryHintStart = prompt.length - promptWithoutDeliveryHint.length;
3904
+ const inputStart = Math.max(promptInputRange.start, promptWithoutDeliveryHintStart);
3905
+ const inputEnd = Math.max(inputStart, Math.min(promptInputRange.end, promptWithoutDeliveryHint.length + promptWithoutDeliveryHintStart));
3906
+ const decoratedPromptSuffixStart = decoratedPrompt.length - promptWithoutDeliveryHint.length;
3907
+ const requestHeader = "Current user request:\n";
3908
+ const requestHeaderStart = decoratedPromptSuffixStart - 22;
3909
+ return {
3910
+ start: inputStart === promptWithoutDeliveryHintStart && decoratedPrompt.slice(requestHeaderStart, decoratedPromptSuffixStart) === requestHeader ? requestHeaderStart : decoratedPromptSuffixStart + inputStart - promptWithoutDeliveryHintStart,
3911
+ end: decoratedPromptSuffixStart + inputEnd - promptWithoutDeliveryHintStart
3912
+ };
3913
+ }
3914
+ function splitLeadingCodexDeliveryHint(prompt) {
3915
+ const trimmedStart = prompt.trimStart();
3916
+ const matchedHint = MESSAGE_TOOL_DELIVERY_HINTS.find((hint) => trimmedStart.startsWith(hint));
3917
+ if (!matchedHint) return { prompt };
3918
+ return {
3919
+ deliveryHint: matchedHint,
3920
+ prompt: trimmedStart.slice(matchedHint.length).replace(/^\s*\n/, "").trimStart()
3921
+ };
3922
+ }
3923
+ //#endregion
3924
+ //#region extensions/codex/src/app-server/run-attempt-state.ts
3925
+ async function clearCodexBindingAfterInvalidImagePayload(bindingStore, identity, fields, expected) {
3926
+ const currentBinding = bindingStore.read(identity);
3927
+ const expectedThreadId = fields.threadId ?? currentBinding?.threadId;
3928
+ if (!expectedThreadId) return;
3929
+ if (currentBinding && currentBinding.threadId !== expectedThreadId) {
3930
+ embeddedAgentLog.warn("codex app-server image payload error detected for unbound thread; preserving thread binding", {
3931
+ ...fields,
3932
+ boundThreadId: currentBinding.threadId
3933
+ });
3934
+ return;
3935
+ }
3936
+ if (expected || currentBinding?.connectionScope === "supervision") {
3739
3937
  embeddedAgentLog.warn("codex app-server image payload error detected for native-owned thread; preserving binding", fields);
3740
3938
  return;
3741
3939
  }
@@ -3784,6 +3982,7 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
3784
3982
  const messages = await readMirroredSessionHistoryMessages({
3785
3983
  ...activeTranscriptTarget,
3786
3984
  signal: connection.runAbortController.signal,
3985
+ contextTokenBudget: effectiveContextTokenBudget,
3787
3986
  ...transcriptReadFence ? { admission: transcriptReadFence } : {}
3788
3987
  });
3789
3988
  connection.runAbortController.signal.throwIfAborted();
@@ -3853,9 +4052,11 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
3853
4052
  });
3854
4053
  historyState.messages = await readFencedHistory() ?? historyState.messages;
3855
4054
  }
4055
+ historyState.messages.push(...params.pluginRuntimeRefreshMessages ?? []);
3856
4056
  const memoryToolNames = getCodexWorkspaceMemoryToolNames(toolBridge.availableSpecs);
3857
4057
  const workspaceBootstrapContext = await buildCodexWorkspaceBootstrapContext({
3858
4058
  params: runtimeParams,
4059
+ agentWorkspaceDeveloperInstructions: connection.mutable.startupBinding?.agentWorkspaceDeveloperInstructions,
3859
4060
  resolvedWorkspace: runtimeParams.bootstrapWorkspaceDir ?? resolvedWorkspace,
3860
4061
  executionWorkspace: resolvedWorkspace,
3861
4062
  effectiveWorkspace,
@@ -3865,7 +4066,7 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
3865
4066
  ringZeroActive: isHostScopedAgentToolActive("openclaw") && isSystemAgentOnlyCodexDynamicToolAllowlist(runtimeParams.toolsAllow),
3866
4067
  sandboxed: sandbox?.enabled === true
3867
4068
  });
3868
- const agentWorkspaceDeveloperInstructions = workspaceBootstrapContext.threadDeveloperInstructions ? connection.mutable.startupBinding?.agentWorkspaceDeveloperInstructions ?? workspaceBootstrapContext.threadDeveloperInstructions : void 0;
4069
+ const agentWorkspaceDeveloperInstructions = workspaceBootstrapContext.threadDeveloperInstructions;
3869
4070
  const baseDeveloperInstructions = joinPresentSections(buildDeveloperInstructions(runtimeParams, { dynamicTools: toolBridge.availableSpecs }), agentWorkspaceDeveloperInstructions);
3870
4071
  const watchedSessionsContext = buildCodexWatchedSessionsContext({
3871
4072
  attempt: runtimeParams,
@@ -4102,6 +4303,28 @@ function isInvalidCodexImagePayloadError(message) {
4102
4303
  return /\b(?:invalid|malformed)\b[\s\S]{0,120}\b(?:image|image url|base64)\b/iu.test(normalizedMessage) || /\b(?:image|image url|base64)\b[\s\S]{0,120}\b(?:invalid|malformed)\b/iu.test(normalizedMessage);
4103
4304
  }
4104
4305
  //#endregion
4306
+ //#region extensions/codex/src/app-server/run-attempt-reply-media.ts
4307
+ async function settleReplyMedia(activeTurn, result, { state, settlementExpired }, signal) {
4308
+ if (activeTurn.prepareReplyMedia && !signal.aborted) {
4309
+ state.pendingSettlementStage = "reply/media";
4310
+ const transferAbort = new AbortController();
4311
+ settlementExpired.then(() => transferAbort.abort(/* @__PURE__ */ new Error("Reply media settlement expired")));
4312
+ try {
4313
+ const prepared = await activeTurn.prepareReplyMedia({
4314
+ kind: "attempt",
4315
+ attempt: result
4316
+ }, transferAbort.signal);
4317
+ if (prepared.kind !== "attempt") throw new Error("Reply media preparation returned the wrong result kind");
4318
+ result.preparedReplyMedia = prepared.preparedMedia;
4319
+ } catch (error) {
4320
+ if (!signal.aborted) throw error;
4321
+ } finally {
4322
+ transferAbort.abort();
4323
+ }
4324
+ }
4325
+ if (signal.aborted) await state.abortCleanup;
4326
+ }
4327
+ //#endregion
4105
4328
  //#region extensions/codex/src/app-server/trajectory.ts
4106
4329
  /** Creates a trajectory recorder when the host exposes its capture capability. */
4107
4330
  function createCodexTrajectoryRecorder(params) {
@@ -4219,6 +4442,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4219
4442
  clearTimeout(drainGraceTimer);
4220
4443
  deadlines.dispose();
4221
4444
  };
4445
+ if (state.pluginRuntimeRefreshStop) await state.pluginRuntimeRefreshStop.catch(() => void 0);
4222
4446
  const settlement = drainNotificationQueue().then(async () => {
4223
4447
  await closeProjection();
4224
4448
  await activeProjector.settlement.drain();
@@ -4238,7 +4462,10 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4238
4462
  } finally {
4239
4463
  if (!state.projectionClosed) await resources.runCleanupStep("codex-transcript-checkpoint", closeProjection);
4240
4464
  }
4241
- const result = activeProjector.buildResult(toolBridge.telemetry, { yieldDetected: toolState.yieldDetected });
4465
+ const result = activeProjector.buildResult(toolBridge.telemetry, {
4466
+ yieldDetected: toolState.yieldDetected,
4467
+ steeringMessages: params.pluginRuntimeRefreshPending?.() ? turnRuntime.steeringQueueRef.current?.getAcceptedMessages() : void 0
4468
+ });
4242
4469
  const projectedTerminal = attemptTerminal.project(result.terminal);
4243
4470
  const isFinalAborted = () => terminalState.explicitCancellationObserved || !resourceState.executionDisconnectError && (projectedTerminal.aborted || runAbortController.signal.aborted && !state.clientClosedAbort);
4244
4471
  const currentPromptError = (fallback) => resourceState.executionDisconnectError ?? state.clientClosedPromptError ?? (state.timeout ? `codex app-server ${state.timeout.kind === "execution" ? "execution budget" : "terminal settlement"} timed out` : fallback);
@@ -4399,7 +4626,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4399
4626
  degradedSettlement
4400
4627
  ]);
4401
4628
  }
4402
- if (runAbortController.signal.aborted) await state.abortCleanup;
4629
+ await settleReplyMedia(activeTurn, result, turnRuntime, runAbortController.signal);
4403
4630
  } finally {
4404
4631
  closeSettlement();
4405
4632
  }
@@ -4483,7 +4710,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4483
4710
  hookRunner
4484
4711
  });
4485
4712
  state.shouldDelayNativeHookRelayUnregister = completedTurnStatus === "completed" && !effectiveTimedOut && !runAbortController.signal.aborted && !finalAborted && !finalPromptError;
4486
- if (turnSucceeded && !runAbortController.signal.aborted) try {
4713
+ if (turnSucceeded && !runAbortController.signal.aborted && !state.pluginRuntimeRefreshStop) try {
4487
4714
  const continuityCalibration = context.promptState.noEngineContinuityProjectionApplied ? buildCodexContinuityCalibration({
4488
4715
  promptChars: prompt.turnState.codexTurnPromptText.length,
4489
4716
  inputTokens: result.attemptUsage?.contextUsage?.state === "available" ? result.attemptUsage.contextUsage.promptTokens ?? 0 : 0
@@ -4528,27 +4755,30 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4528
4755
  });
4529
4756
  markTrajectoryEndRecorded();
4530
4757
  const terminalAssistantText = collectTerminalAssistantText(result);
4531
- if (terminalAssistantText && (!streamState.eventEmitted || streamState.needsTerminalSnapshot) && !finalAborted && !finalPromptError) emitCodexAppServerEvent(params, {
4758
+ if (terminalAssistantText && (assistantTranscriptIdempotencyKey || (!streamState.eventEmitted || streamState.needsTerminalSnapshot) && !finalAborted && !finalPromptError)) emitCodexAppServerEvent(params, {
4532
4759
  stream: "assistant",
4533
- data: { text: terminalAssistantText }
4760
+ data: {
4761
+ text: terminalAssistantText,
4762
+ ...assistantTranscriptIdempotencyKey ? {
4763
+ itemId: assistantTranscriptIdempotencyKey,
4764
+ replace: true,
4765
+ replaceable: true
4766
+ } : {}
4767
+ }
4534
4768
  });
4535
- emitLifecycleTerminal(finalPromptError ? {
4536
- phase: "error",
4537
- error: formatErrorMessage(finalPromptError),
4538
- ...buildLifecycleTerminalMeta({
4539
- aborted: finalAborted,
4540
- timedOut: effectiveTimedOut
4541
- })
4542
- } : {
4543
- phase: "end",
4769
+ emitLifecycleTerminal({
4770
+ phase: finalPromptError ? "error" : "end",
4771
+ ...finalPromptError ? { error: formatErrorMessage(finalPromptError) } : {},
4772
+ ...assistantTranscriptIdempotencyKey ? { assistantTranscriptIdempotencyKey } : {},
4544
4773
  ...buildLifecycleTerminalMeta({
4545
4774
  aborted: finalAborted,
4546
4775
  timedOut: effectiveTimedOut,
4547
- yielded: toolState.yieldDetected
4776
+ yielded: finalPromptError ? void 0 : toolState.yieldDetected
4548
4777
  })
4549
4778
  });
4550
4779
  const finalizedResult = Object.assign(result, {
4551
4780
  ...runtimeModelSelection ? { runtimeModelSelection } : {},
4781
+ ...turnSucceeded && params.pluginRuntimeRefreshPending?.() ? { pluginRuntimeRefreshMessages: params.suppressNextUserMessagePersistence && !params.pluginRuntimeRefreshMessages ? [params.userTurnTranscriptRecorder?.getPersistedMessage?.() ?? buildCodexUserPromptMessage(params), ...result.messagesSnapshot] : result.messagesSnapshot } : {},
4552
4782
  ...toolState.yieldAcknowledgment ? { yieldAcknowledgment: toolState.yieldAcknowledgment } : {},
4553
4783
  ...codexAppServerFailure ? { codexAppServerFailure } : {},
4554
4784
  ...promptTimeoutOutcome ? { promptTimeoutOutcome } : {},
@@ -4675,7 +4905,7 @@ function createCodexAttemptLifecycleController(resources, turnRuntime) {
4675
4905
  state.pendingTerminalDynamicToolRelease = value;
4676
4906
  scheduleTerminalDynamicToolReleaseCheck();
4677
4907
  };
4678
- const emitLifecycleStart = () => {
4908
+ const emitLifecycleStart = (model) => {
4679
4909
  emitCodexAppServerEvent(params, {
4680
4910
  stream: "lifecycle",
4681
4911
  data: {
@@ -4683,10 +4913,17 @@ function createCodexAttemptLifecycleController(resources, turnRuntime) {
4683
4913
  startedAt: attemptStartedAt
4684
4914
  }
4685
4915
  });
4916
+ emitCodexAppServerEvent(params, {
4917
+ stream: "lifecycle",
4918
+ data: {
4919
+ phase: "model",
4920
+ ...model
4921
+ }
4922
+ });
4686
4923
  state.lifecycleStarted = true;
4687
4924
  };
4688
4925
  const emitLifecycleTerminal = (data) => {
4689
- if (!state.lifecycleStarted || state.lifecycleTerminalEmitted || state.permissionChangeRestart) return;
4926
+ if (!state.lifecycleStarted || state.lifecycleTerminalEmitted || state.permissionChangeRestart || params.pluginRuntimeRefreshPending?.()) return;
4690
4927
  emitCodexAppServerEvent(params, {
4691
4928
  stream: "lifecycle",
4692
4929
  data: {
@@ -4784,13 +5021,29 @@ function createCodexAttemptLifecycleController(resources, turnRuntime) {
4784
5021
  //#endregion
4785
5022
  //#region extensions/codex/src/app-server/run-attempt-notification-controller.ts
4786
5023
  function createCodexAttemptNotificationController(resources, turnRuntime, lifecycle) {
4787
- const { prompt, state: resourceState, projectorRef, registerNativeSubagentMonitor } = resources;
5024
+ const { prompt, state: resourceState, projectorRef } = resources;
4788
5025
  const { context, turnState } = prompt;
4789
5026
  const { attemptTools, runtime } = context;
4790
5027
  const { appServer, runAbortController } = runtime.connection;
4791
5028
  const { allocateCodexToolOutcomeOrdinal } = attemptTools;
4792
- const { state, turnIdRef, userInputBridgeRef, steeringQueueRef, activeTurnItemIds, pendingOpenClawDynamicToolCompletionIds, openClawDynamicToolExecutions, completeTurn, noteProgress, deadlines } = turnRuntime;
5029
+ const { state, turnIdRef, steeringQueueRef, activeTurnItemIds, pendingOpenClawDynamicToolCompletionIds, openClawDynamicToolExecutions, completeTurn, noteProgress, deadlines } = turnRuntime;
4793
5030
  const { scheduleTerminalDynamicToolReleaseCheck, reportExecutionNotification, maybeAnnounceFastModeAutoOff } = lifecycle;
5031
+ const pendingNativeCommandItems = /* @__PURE__ */ new Set();
5032
+ const nativeItemObservers = /* @__PURE__ */ new Set();
5033
+ const waitForNativeTerminalItems = async (signal) => {
5034
+ if (pendingNativeCommandItems.size === 0) return;
5035
+ const completion = createDeferred();
5036
+ const observe = () => {
5037
+ if (pendingNativeCommandItems.size === 0) completion.resolve();
5038
+ };
5039
+ nativeItemObservers.add(observe);
5040
+ try {
5041
+ observe();
5042
+ if (!await waitForPromiseOrAbort(completion.promise, signal)) signal.throwIfAborted();
5043
+ } finally {
5044
+ nativeItemObservers.delete(observe);
5045
+ }
5046
+ };
4794
5047
  const isTerminalTurnNotificationForTurn = (notification, notificationTurnId) => isTerminalCodexTurnNotificationForTurn({
4795
5048
  notification,
4796
5049
  threadId: resourceState.thread.threadId,
@@ -4800,7 +5053,6 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
4800
5053
  if (state.projectionClosed) return;
4801
5054
  const projector = projectorRef.current;
4802
5055
  const turnId = turnIdRef.current;
4803
- userInputBridgeRef.current?.handleNotification(notification);
4804
5056
  if (!projector || !turnId) {
4805
5057
  if (notification.method === "error") state.latestStartupErrorNotification = notification;
4806
5058
  return;
@@ -4845,6 +5097,11 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
4845
5097
  });
4846
5098
  } finally {
4847
5099
  state.activeLocalProjections -= 1;
5100
+ if (isCurrentTurn && notification.method === "item/completed") {
5101
+ const item = readCodexNotificationItem(notification.params);
5102
+ if (item?.type === "commandExecution" && typeof item.id === "string") pendingNativeCommandItems.delete(item.id);
5103
+ }
5104
+ for (const observe of nativeItemObservers) observe();
4848
5105
  if (isTerminal) {
4849
5106
  if ((readCodexTurnCompletedNotification(notification.params)?.turn)?.status === "interrupted" && state.sawCodexInterruptMarker) projector.markAborted();
4850
5107
  completeTurn();
@@ -4883,6 +5140,7 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
4883
5140
  const modelToolCallId = readRawResponseToolCallId(notification);
4884
5141
  if (modelToolCallId) allocateCodexToolOutcomeOrdinal?.(modelToolCallId);
4885
5142
  const nativeItem = readCodexNotificationItem(notification.params);
5143
+ if (notification.method === "item/started" && nativeItem?.type === "commandExecution" && typeof nativeItem.id === "string") pendingNativeCommandItems.add(nativeItem.id);
4886
5144
  if (nativeItem?.type === "webSearch") {
4887
5145
  projector.settlement.turnTainted ||= notification.method === "item/completed";
4888
5146
  projector.recordNativeToolOutcome(nativeItem);
@@ -4899,9 +5157,9 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
4899
5157
  const drainNotificationQueue = async () => {
4900
5158
  await resourceState.turnRoute?.drain();
4901
5159
  };
4902
- registerNativeSubagentMonitor(resourceState.thread.threadId);
4903
5160
  return {
4904
5161
  waitForActiveNativeTurnCompletion,
5162
+ waitForNativeTerminalItems,
4905
5163
  noteNotificationReceived,
4906
5164
  enqueueNotification,
4907
5165
  drainNotificationQueue
@@ -4917,13 +5175,24 @@ async function prepareCodexAttemptPrompt(context) {
4917
5175
  const { connection, buildActiveRunAttemptParams, effectiveContextTokenBudget, effectiveRuntimeModelId, effectiveRuntimeProviderId } = runtime;
4918
5176
  const { params, activeContextEngine, usesSupervisionConnection, mutable, isInactiveThreadBootstrapBinding, bindingStore, bindingIdentity, agentDir, appServer, contextSessionKey, effectiveWorkspace, sandbox } = connection;
4919
5177
  const { toolBridge } = attemptTools;
4920
- let contextImages = [];
4921
- const currentUserTurnIdempotencyKey = params.userTurnTranscriptRecorder?.message?.idempotencyKey;
5178
+ const nativeHistoryProvenancePrefix = params.trigger === "user" ? buildCodexHistoryProvenancePrefix(params) : void 0;
5179
+ const forkedSession = !mutable.startupBinding?.threadId && params.sessionTarget ? getSessionEntry({
5180
+ ...params.sessionTarget,
5181
+ sessionKey: contextSessionKey,
5182
+ hydrateSkillPromptRefs: false,
5183
+ readConsistency: "latest"
5184
+ }) : void 0;
5185
+ const preserveForkedToolResults = Boolean(forkedSession?.sessionId === params.sessionId && forkedSession.createdVia === "spawn" && forkedSession.parentSessionKey && forkedSession.parentSessionKey !== contextSessionKey && forkedSession.forkSource?.sessionKey === forkedSession.parentSessionKey && forkedSession.forkSource.sessionId !== params.sessionId);
5186
+ let contextImageGroups = [];
5187
+ let turnContextImageGroups = [];
4922
5188
  const assertProjectionCurrent = () => {
4923
5189
  params.hostCapabilities.assertActive();
4924
5190
  connection.assertCurrent();
4925
5191
  connection.runAbortController.signal.throwIfAborted();
4926
5192
  };
5193
+ const admittedMessage = params.userTurnTranscriptRecorder?.message ?? await params.userTurnTranscriptRecorder?.resolveMessage();
5194
+ assertProjectionCurrent();
5195
+ const currentUserTurnIdempotencyKey = params.pluginRuntimeRefreshMessages ? void 0 : admittedMessage?.idempotencyKey;
4927
5196
  const prepareFileContext = async (message, maxChars) => {
4928
5197
  const prepare = params.hostCapabilities.prepareContextMedia;
4929
5198
  if (!prepare) {
@@ -4949,11 +5218,12 @@ async function prepareCodexAttemptPrompt(context) {
4949
5218
  originalHistoryMessages: historyState.messages,
4950
5219
  prompt: params.prompt,
4951
5220
  maxRenderedContextChars: codexContinuityProjectionMaxChars,
5221
+ toolPayloadMode: params.pluginRuntimeRefreshMessages || preserveForkedToolResults ? "preserve" : "elide",
4952
5222
  prepareFileContext,
4953
5223
  currentUserTurnIdempotencyKey
4954
5224
  });
4955
5225
  assertProjectionCurrent();
4956
- contextImages = projection.images ?? [];
5226
+ contextImageGroups = projection.imageGroups ?? [];
4957
5227
  promptState.promptText = projection.promptText;
4958
5228
  promptState.promptContextRange = projection.promptContextRange;
4959
5229
  promptState.prePromptMessageCount = projection.prePromptMessageCount;
@@ -4998,12 +5268,12 @@ async function prepareCodexAttemptPrompt(context) {
4998
5268
  prompt: params.prompt,
4999
5269
  systemPromptAddition: assembled.systemPromptAddition,
5000
5270
  maxRenderedContextChars: codexContextProjectionMaxChars,
5001
- toolPayloadMode: contextEngineProjection ? "preserve" : "elide",
5271
+ toolPayloadMode: contextEngineProjection || params.pluginRuntimeRefreshMessages || preserveForkedToolResults ? "preserve" : "elide",
5002
5272
  ...projectionDecision.project ? { prepareFileContext } : {},
5003
5273
  currentUserTurnIdempotencyKey
5004
5274
  });
5005
5275
  assertProjectionCurrent();
5006
- contextImages = projectionDecision.project ? projection.images ?? [] : [];
5276
+ contextImageGroups = projectionDecision.project ? projection.imageGroups ?? [] : [];
5007
5277
  const decisionBinding = decisionStartupBinding;
5008
5278
  embeddedAgentLog.info("codex app-server context-engine projection decision", {
5009
5279
  sessionId: params.sessionId,
@@ -5031,13 +5301,17 @@ async function prepareCodexAttemptPrompt(context) {
5031
5301
  if (activeContextEngine) try {
5032
5302
  await applyActiveContextEngineProjection(runtime.nativeToolSurfaceEnabled ? mutable.startupBinding : void 0);
5033
5303
  } catch (assembleErr) {
5034
- if (assembleErr instanceof CodexContextAttachmentError) throw assembleErr;
5304
+ if (assembleErr instanceof CodexContextAttachmentError || params.pluginRuntimeRefreshMessages) throw assembleErr;
5035
5305
  assertProjectionCurrent();
5036
5306
  embeddedAgentLog.warn("context engine assemble failed; using Codex baseline prompt", { error: formatErrorMessage(assembleErr) });
5037
5307
  }
5038
5308
  const codexModelInputHistoryMessages = [];
5309
+ const admittedContent = admittedMessage?.content;
5310
+ const currentUserMessage = admittedMessage ? typeof admittedContent === "string" ? admittedContent : (admittedContent ?? []).flatMap((part) => part.type === "text" ? [part.text] : []).join("\n") : params.pluginRuntimeRefreshMessages ? "" : params.prompt;
5039
5311
  const buildPromptFromCurrentInputs = async () => {
5040
5312
  return await resolveAgentHarnessBeforePromptBuildResult({
5313
+ currentUserMessage,
5314
+ currentUserMessageId: admittedMessage?.idempotencyKey,
5041
5315
  prompt: prependCurrentInboundContext(promptState.promptText, params.currentInboundContext),
5042
5316
  developerInstructions: { build: ({ toolsAllow }) => {
5043
5317
  if (isRestrictivePromptToolsAllow(toolsAllow)) throw new Error("Codex app-server cannot enforce before_prompt_build toolsAllow; use the embedded or Copilot runtime for turn-scoped tool policy.");
@@ -5087,12 +5361,21 @@ async function prepareCodexAttemptPrompt(context) {
5087
5361
  promptInputRange: promptBuildResult.promptInputRange,
5088
5362
  decoratedPrompt: turnPromptText
5089
5363
  });
5090
- return fitCodexProjectedContextForTurnStart({
5364
+ const imageOffset = projectedRanges && promptState.promptContextRange ? projectedRanges.contextRange.start - promptState.promptContextRange.start : void 0;
5365
+ const fitted = fitCodexProjectedContextForTurnStart({
5091
5366
  promptText: turnPromptText,
5367
+ maxChars: CODEX_TURN_START_TEXT_INPUT_MAX_CHARS - (nativeHistoryProvenancePrefix?.length ?? 0),
5092
5368
  contextRange: projectedRanges?.contextRange,
5093
5369
  requestRange: projectedRanges?.requestRange,
5094
- preservedRange
5370
+ preservedRange,
5371
+ imageGroups: imageOffset === void 0 ? [] : contextImageGroups.map((group) => ({
5372
+ images: group.images,
5373
+ start: group.start + imageOffset,
5374
+ end: group.end + imageOffset
5375
+ }))
5095
5376
  });
5377
+ turnContextImageGroups = fitted.imageGroups ?? [];
5378
+ return fitted.promptText;
5096
5379
  };
5097
5380
  const firstPromptBuild = await buildPromptFromCurrentInputs();
5098
5381
  const turnState = {
@@ -5148,8 +5431,8 @@ async function prepareCodexAttemptPrompt(context) {
5148
5431
  return projected;
5149
5432
  };
5150
5433
  const applyNoContextEngineContinuityProjection = async (action, binding) => {
5151
- const hasContinuity = historyState.messages.some((message) => message.role === "user" || isCodexDurableCustomMessage(message) || action === "started" && (message.role === "compactionSummary" || message.role === "branchSummary"));
5152
- if (activeContextEngine || !hasContinuity) return false;
5434
+ const hasContinuity = historyState.messages.some((message) => message.role === "user" || isCodexDurableCustomMessage(message) || action === "started" && (message.role === "compactionSummary" || message.role === "branchSummary" || message.role === "assistant" && message.content.some((part) => part.type === "text" && part.text.trim())));
5435
+ if (activeContextEngine || !hasContinuity && !params.pluginRuntimeRefreshMessages?.length) return false;
5153
5436
  if (action === "resumed" && promptState.precomputedStaleBindingContinuityProjectionApplied) return true;
5154
5437
  if (action === "started" && promptState.staleBindingContinuityForcedFreshStart) return true;
5155
5438
  if (action === "started" && promptState.inactiveThreadBootstrapBindingForcedFreshStart) return false;
@@ -5171,9 +5454,8 @@ async function prepareCodexAttemptPrompt(context) {
5171
5454
  binding,
5172
5455
  bindingStore,
5173
5456
  identity: bindingIdentity,
5174
- sessionFile: params.sessionFile,
5175
5457
  agentDir,
5176
- codexHome: appServer.start.env?.CODEX_HOME,
5458
+ codexHome: appServer.start.codexHome ?? appServer.start.env?.CODEX_HOME,
5177
5459
  config: params.config,
5178
5460
  contextEngineActive: Boolean(activeContextEngine),
5179
5461
  expectedSessionRuntimeOwnership: params.expectedSessionRuntimeOwnership,
@@ -5193,7 +5475,7 @@ async function prepareCodexAttemptPrompt(context) {
5193
5475
  try {
5194
5476
  await applyActiveContextEngineProjection(void 0);
5195
5477
  } catch (assembleErr) {
5196
- if (assembleErr instanceof CodexContextAttachmentError) throw assembleErr;
5478
+ if (assembleErr instanceof CodexContextAttachmentError || params.pluginRuntimeRefreshMessages) throw assembleErr;
5197
5479
  assertProjectionCurrent();
5198
5480
  embeddedAgentLog.warn("context engine assemble failed; using Codex baseline prompt", { error: formatErrorMessage(assembleErr) });
5199
5481
  }
@@ -5222,10 +5504,11 @@ async function prepareCodexAttemptPrompt(context) {
5222
5504
  let workspaceReferencesIncluded = true;
5223
5505
  return {
5224
5506
  context,
5225
- get contextImages() {
5226
- return contextImages;
5507
+ get contextImageGroups() {
5508
+ return turnContextImageGroups;
5227
5509
  },
5228
5510
  codexModelInputHistoryMessages,
5511
+ nativeHistoryProvenancePrefix,
5229
5512
  turnState,
5230
5513
  refreshWorkspaceReferences: (include) => {
5231
5514
  turnState.codexTurnPromptText = decorateCodexTurnPromptText(turnState.promptBuild, include);
@@ -5266,6 +5549,13 @@ function prepareCodexAttemptResources(prompt) {
5266
5549
  const { connection, hookChannelId } = runtime;
5267
5550
  const { appServer, params, effectiveCwd, sessionAgentId, contextSessionKey, runAbortController, sandbox, options, nativeHookRelayEvents } = connection;
5268
5551
  const { toolBridge } = attemptTools;
5552
+ let nativeProcessAuthorityReleased = false;
5553
+ const releaseNativeProcessAuthority = () => {
5554
+ if (!nativeProcessAuthorityReleased) {
5555
+ nativeProcessAuthorityReleased = true;
5556
+ nativeProcessAuthority?.release();
5557
+ }
5558
+ };
5269
5559
  const trajectoryRecorder = createCodexTrajectoryRecorder({
5270
5560
  attempt: params,
5271
5561
  cwd: effectiveCwd,
@@ -5290,6 +5580,7 @@ function prepareCodexAttemptResources(prompt) {
5290
5580
  nativePreToolUseFailureFallbackTerminalReason: void 0,
5291
5581
  releaseSharedClientLease: void 0,
5292
5582
  startupClientUnsafe: false,
5583
+ turnStartAttempted: false,
5293
5584
  sharedCodexClientRetiredForOneShotCleanup: false,
5294
5585
  sandboxExecEnvironment: void 0,
5295
5586
  executionDisconnectError: void 0,
@@ -5380,12 +5671,16 @@ function prepareCodexAttemptResources(prompt) {
5380
5671
  await operation();
5381
5672
  }
5382
5673
  });
5383
- const unregisterNativeSubagentMonitor = () => {
5384
- state.nativeSubagentMonitor?.unregister();
5674
+ let nativeSubagentMonitorSettlement;
5675
+ const unregisterNativeSubagentMonitor = async () => {
5676
+ const registration = state.nativeSubagentMonitor;
5385
5677
  state.nativeSubagentMonitor = void 0;
5678
+ if (registration) nativeSubagentMonitorSettlement = registration.unregister();
5679
+ await nativeSubagentMonitorSettlement;
5386
5680
  };
5387
- const registerNativeSubagentMonitor = (parentThreadId) => {
5388
- unregisterNativeSubagentMonitor();
5681
+ const registerNativeSubagentMonitor = async (parentThreadId) => {
5682
+ await unregisterNativeSubagentMonitor();
5683
+ connection.assertCurrent();
5389
5684
  const sessionKey = params.sessionKey;
5390
5685
  const storePath = params.sessionTarget?.storePath;
5391
5686
  const parentSession = sessionKey && storePath ? getSessionEntry({
@@ -5395,17 +5690,46 @@ function prepareCodexAttemptResources(prompt) {
5395
5690
  readConsistency: "latest",
5396
5691
  hydrateSkillPromptRefs: false
5397
5692
  }) : void 0;
5693
+ const historyOwner = createCodexNativeSubagentHistoryOwner({
5694
+ parentThreadId,
5695
+ sessionId: params.sessionId,
5696
+ ...parentSession?.sessionId === params.sessionId ? { lifecycleRevision: parentSession.lifecycleRevision } : {},
5697
+ binding: state.thread
5698
+ });
5699
+ const { bindingStore, bindingIdentity } = connection;
5700
+ const submissionStore = historyOwner ? {
5701
+ assertCurrent: () => {
5702
+ const current = bindingStore.read(bindingIdentity);
5703
+ if (!current || !matchesCodexNativeSubagentSubmissionBinding(current, historyOwner)) throw new Error("Native submission binding is no longer current.");
5704
+ if (historyOwner.lifecycleRevision && sessionKey && storePath) {
5705
+ if (getSessionEntry({
5706
+ agentId: sessionAgentId,
5707
+ sessionKey,
5708
+ storePath,
5709
+ readConsistency: "latest",
5710
+ hydrateSkillPromptRefs: false
5711
+ })?.lifecycleRevision !== historyOwner.lifecycleRevision) throw new Error("Native submission session lifecycle is no longer current.");
5712
+ }
5713
+ },
5714
+ read: () => bindingStore.readNativeSubagentSubmissions(bindingIdentity, historyOwner),
5715
+ record: (receipt, assertCurrent) => bindingStore.mutate(bindingIdentity, {
5716
+ kind: "record-native-subagent-submission",
5717
+ owner: historyOwner,
5718
+ receipt
5719
+ }, assertCurrent),
5720
+ consume: (receipt, assertCurrent) => bindingStore.mutate(bindingIdentity, {
5721
+ kind: "consume-native-subagent-submission",
5722
+ owner: historyOwner,
5723
+ receipt
5724
+ }, assertCurrent)
5725
+ } : void 0;
5398
5726
  state.nativeSubagentMonitor = codexNativeSubagentMonitorRuntime.register({
5399
5727
  client: state.client,
5400
5728
  parentThreadId,
5401
5729
  requesterSessionKey: params.sessionKey,
5402
5730
  taskRuntimeScope: params.agentHarnessTaskRuntimeScope,
5403
- historyOwner: createCodexNativeSubagentHistoryOwner({
5404
- parentThreadId,
5405
- sessionId: params.sessionId,
5406
- ...parentSession?.sessionId === params.sessionId ? { lifecycleRevision: parentSession.lifecycleRevision } : {},
5407
- binding: state.thread
5408
- }),
5731
+ historyOwner,
5732
+ submissionStore,
5409
5733
  agentId: sessionAgentId,
5410
5734
  retainClient: () => retainSharedCodexAppServerClientIfCurrent(state.client),
5411
5735
  retainParentThread: (protectedThreadId) => protectCodexAppServerLiveThread(state.client, protectedThreadId),
@@ -5416,7 +5740,7 @@ function prepareCodexAttemptResources(prompt) {
5416
5740
  } } : {}
5417
5741
  });
5418
5742
  };
5419
- const releaseCurrentRoute = () => {
5743
+ const releaseCurrentRoute = async () => {
5420
5744
  state.releaseInferenceContext?.();
5421
5745
  state.releaseInferenceContext = void 0;
5422
5746
  state.detachRouteAbort();
@@ -5424,7 +5748,89 @@ function prepareCodexAttemptResources(prompt) {
5424
5748
  state.turnRoute?.release();
5425
5749
  state.turnRoute = void 0;
5426
5750
  state.routeActivated = false;
5427
- unregisterNativeSubagentMonitor();
5751
+ await unregisterNativeSubagentMonitor();
5752
+ };
5753
+ let subscriptionSettlement;
5754
+ const retainThreadSubscription = async () => {
5755
+ const { client, thread } = state;
5756
+ if (subscriptionSettlement?.thread === thread) return subscriptionSettlement.retained;
5757
+ if (!thread || thread.clientId !== resolveCodexAppServerClientInstanceId(client)) return false;
5758
+ const { bindingStore, bindingIdentity } = connection;
5759
+ const retained = await bindingStore.withLease(bindingIdentity, async () => {
5760
+ if (!isSameCodexAppServerThreadOwner(bindingStore.read(bindingIdentity), thread)) return false;
5761
+ try {
5762
+ if (!state.turnStartAttempted) runAbortController.signal.throwIfAborted();
5763
+ if (!hasCodexNativeBackgroundProcesses(client, thread.threadId)) {
5764
+ params.hostCapabilities.assertActive();
5765
+ connection.assertCurrent();
5766
+ }
5767
+ thread.liveThreadOwnership?.assertCurrent();
5768
+ } catch {
5769
+ return false;
5770
+ }
5771
+ return await retainCodexAppServerBindingSubscription(client, thread.threadId, {
5772
+ release: thread.liveThreadOwnership?.release,
5773
+ configFingerprint: thread.liveThreadConfigFingerprint,
5774
+ serviceTier: state.turnStartAttempted ? connection.mutable.pluginAppServer.serviceTier : thread.liveThreadOwnership?.serviceTier,
5775
+ ephemeralPolicy: thread.liveThreadEphemeralPolicy
5776
+ });
5777
+ });
5778
+ if (retained) subscriptionSettlement = {
5779
+ thread,
5780
+ retained: true
5781
+ };
5782
+ return retained;
5783
+ };
5784
+ const releaseThreadSubscription = async (assertCurrent) => {
5785
+ const { client, thread } = state;
5786
+ if (!thread || subscriptionSettlement?.thread === thread) return true;
5787
+ subscriptionSettlement = {
5788
+ thread,
5789
+ retained: false
5790
+ };
5791
+ if (thread.liveThreadOwnership) try {
5792
+ await thread.liveThreadOwnership.release(thread.threadId, assertCurrent);
5793
+ return true;
5794
+ } catch (error) {
5795
+ await closeCodexStartupClientBestEffort(client);
5796
+ throw error;
5797
+ }
5798
+ const released = await unsubscribeCodexThreadBestEffort(client, {
5799
+ threadId: thread.threadId,
5800
+ timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS,
5801
+ assertCurrent
5802
+ });
5803
+ if (!released) await closeCodexStartupClientBestEffort(client);
5804
+ return released;
5805
+ };
5806
+ const cleanupBeforeActiveTurn = async () => {
5807
+ await runCleanupStep("codex-pre-turn-hook-fallback", activateNativePreToolUseFailureFallback);
5808
+ await runCleanupStep("codex-pre-turn-subscription", async () => {
5809
+ const { thread } = state;
5810
+ if (!thread || subscriptionSettlement?.thread === thread || state.startupClientUnsafe) return;
5811
+ let retained = false;
5812
+ try {
5813
+ retained = Boolean(!state.turnStartAttempted && !runAbortController.signal.aborted && params.cleanupBundleMcpOnRunEnd !== true && thread.liveThreadOwnership && await retainThreadSubscription());
5814
+ } finally {
5815
+ if (!retained) {
5816
+ if (!isIncognitoSessionKey(params.sessionKey) || await connection.bindingStore.mutate(connection.bindingIdentity, {
5817
+ kind: "clear",
5818
+ threadId: thread.threadId
5819
+ })) await releaseThreadSubscription();
5820
+ }
5821
+ }
5822
+ });
5823
+ await runCleanupStep("codex-pre-turn-route-release", releaseCurrentRoute);
5824
+ const relay = state.nativeHookRelay;
5825
+ state.nativeHookRelay = void 0;
5826
+ await runCleanupStep("codex-pre-turn-native-hook-relay", async () => {
5827
+ relay?.unregister();
5828
+ await relay?.drain();
5829
+ });
5830
+ await runCleanupStep("codex-pre-turn-sandbox-release", releaseSandboxExecEnvironment);
5831
+ await runCleanupStep("codex-pre-turn-source-release", releaseNativeProcessAuthority);
5832
+ await runCleanupStep("codex-pre-turn-trajectory-flush", () => trajectoryRecorder?.flush());
5833
+ await runCleanupStep("codex-pre-turn-shared-client-release", releaseSharedClientLeaseAndRetireOneShotClient);
5428
5834
  };
5429
5835
  const startupTimeoutMs = resolveCodexStartupTimeoutMs({
5430
5836
  timeoutMs: params.timeoutMs,
@@ -5432,8 +5838,13 @@ function prepareCodexAttemptResources(prompt) {
5432
5838
  });
5433
5839
  const requesterChannel = params.messageChannel ?? params.messageProvider;
5434
5840
  const requester = buildCodexHookRequester(params);
5435
- const buildNativeHookRelayFinalConfigPatch = (decision) => {
5436
- state.nativeHookRelay?.unregister();
5841
+ const buildNativeHookRelayFinalConfigPatch = async (decision) => {
5842
+ const previousRelay = state.nativeHookRelay;
5843
+ previousRelay?.unregister();
5844
+ await previousRelay?.drain();
5845
+ connection.assertCurrent();
5846
+ const requiresProcessAdmission = nativeProcessAuthority && runtime.nativeToolSurfaceEnabled;
5847
+ const relayEvents = requiresProcessAdmission && !nativeHookRelayEvents.includes("pre_tool_use") ? [...nativeHookRelayEvents, "pre_tool_use"] : nativeHookRelayEvents;
5437
5848
  if (params.pluginHarnessToolPolicyRestricted === true) {
5438
5849
  state.nativeHookRelay = void 0;
5439
5850
  return {
@@ -5442,10 +5853,13 @@ function prepareCodexAttemptResources(prompt) {
5442
5853
  };
5443
5854
  }
5444
5855
  state.nativeHookRelay = createCodexNativeHookRelay({
5445
- options: options.nativeHookRelay,
5856
+ options: requiresProcessAdmission ? {
5857
+ ...options.nativeHookRelay,
5858
+ enabled: true
5859
+ } : options.nativeHookRelay,
5446
5860
  generation: decision.action === "resume" ? decision.binding.nativeHookRelayGeneration : void 0,
5447
5861
  generationMismatchGraceMs: decision.action === "resume" && !decision.binding.nativeHookRelayGeneration ? CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS : void 0,
5448
- events: nativeHookRelayEvents,
5862
+ events: relayEvents,
5449
5863
  agentId: sessionAgentId,
5450
5864
  sessionId: params.sessionId,
5451
5865
  sessionKey: contextSessionKey,
@@ -5469,6 +5883,10 @@ function prepareCodexAttemptResources(prompt) {
5469
5883
  loopDetectionPreToolUseRelay: appServer.loopDetectionPreToolUseRelay,
5470
5884
  signal: runAbortController.signal,
5471
5885
  hostCapabilities: params.hostCapabilities,
5886
+ nativeProcessAuthority: requiresProcessAdmission ? {
5887
+ owner: nativeProcessAuthority,
5888
+ client: () => state.client
5889
+ } : void 0,
5472
5890
  assertCurrent: connection.assertCurrent,
5473
5891
  onPreToolUseFailure: (failure) => {
5474
5892
  const projector = projectorRef.current;
@@ -5477,21 +5895,40 @@ function prepareCodexAttemptResources(prompt) {
5477
5895
  else pendingNativePreToolUseFailures.push(failure);
5478
5896
  }
5479
5897
  });
5898
+ await state.nativeHookRelay?.prepareInvocation();
5899
+ connection.assertCurrent();
5480
5900
  return {
5481
5901
  configPatch: state.nativeHookRelay ? buildCodexNativeHookRelayConfig({
5482
5902
  relay: state.nativeHookRelay,
5483
- events: nativeHookRelayEvents,
5903
+ events: relayEvents,
5484
5904
  hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec
5485
5905
  }) : options.nativeHookRelay?.enabled === false ? buildCodexNativeHookRelayDisabledConfig() : void 0,
5486
5906
  nativeHookRelayGeneration: state.nativeHookRelay?.generation
5487
5907
  };
5488
5908
  };
5909
+ const nativeProcessAuthority = sandbox?.enabled && sandbox.backend && params.hostCapabilities.retainSourceAuthority ? new CodexNativeProcessAuthority(params.hostCapabilities, (error) => {
5910
+ const message = formatErrorMessage(error);
5911
+ embeddedAgentLog.warn("codex native background work remains unsettled", {
5912
+ runId: params.runId,
5913
+ sessionId: params.sessionId,
5914
+ error: message
5915
+ });
5916
+ emitCodexAppServerEvent(params, {
5917
+ stream: "codex_app_server.lifecycle",
5918
+ data: {
5919
+ phase: "background_cleanup_failed",
5920
+ error: message
5921
+ }
5922
+ });
5923
+ }) : void 0;
5489
5924
  return {
5490
5925
  prompt,
5491
5926
  trajectoryRecorder,
5492
5927
  state,
5493
5928
  projectorRef,
5494
5929
  pendingNativePreToolUseFailures,
5930
+ nativeProcessAuthority,
5931
+ releaseNativeProcessAuthority,
5495
5932
  markTrajectoryEndRecorded: () => {
5496
5933
  state.trajectoryEndRecorded = true;
5497
5934
  },
@@ -5502,6 +5939,9 @@ function prepareCodexAttemptResources(prompt) {
5502
5939
  runCleanupStep,
5503
5940
  registerNativeSubagentMonitor,
5504
5941
  releaseCurrentRoute,
5942
+ retainThreadSubscription,
5943
+ releaseThreadSubscription,
5944
+ cleanupBeforeActiveTurn,
5505
5945
  startupTimeoutMs,
5506
5946
  buildNativeHookRelayFinalConfigPatch
5507
5947
  };
@@ -5509,7 +5949,7 @@ function prepareCodexAttemptResources(prompt) {
5509
5949
  //#endregion
5510
5950
  //#region extensions/codex/src/app-server/run-attempt-route.ts
5511
5951
  async function prepareCodexAttemptRoute(resources, turnRuntime, notifications, handleServerRequest) {
5512
- const { prompt, state: resourceState, trajectoryRecorder, releaseCurrentRoute, registerNativeSubagentMonitor, activateNativePreToolUseFailureFallback, releaseSandboxExecEnvironment, releaseSharedClientLeaseOnce } = resources;
5952
+ const { prompt, state: resourceState, trajectoryRecorder, releaseCurrentRoute, registerNativeSubagentMonitor } = resources;
5513
5953
  const { connection } = prompt.context.runtime;
5514
5954
  const { runAbortController } = connection;
5515
5955
  const { state, turnIdRef, completeTurn } = turnRuntime;
@@ -5542,12 +5982,12 @@ async function prepareCodexAttemptRoute(resources, turnRuntime, notifications, h
5542
5982
  };
5543
5983
  const ensureCurrentThreadRoute = async () => {
5544
5984
  if (resourceState.turnRoute?.threadId !== resourceState.thread.threadId) {
5545
- releaseCurrentRoute();
5985
+ await releaseCurrentRoute();
5546
5986
  resourceState.turnRoute = resourceState.turnRouter.reserveThread({ threadId: resourceState.thread.threadId });
5547
5987
  }
5548
5988
  if (!resourceState.turnRoute) throw new Error("codex app-server turn route was not reserved");
5549
5989
  if (!resourceState.routeActivated) {
5550
- if (!resourceState.nativeSubagentMonitor) registerNativeSubagentMonitor(resourceState.thread.threadId);
5990
+ if (!resourceState.nativeSubagentMonitor) await registerNativeSubagentMonitor(resourceState.thread.threadId);
5551
5991
  resourceState.detachRouteAbort = attachRouteAbort(resourceState.turnRoute);
5552
5992
  await resourceState.turnRoute.activate({
5553
5993
  onNotificationReceived: noteNotificationReceived,
@@ -5558,16 +5998,7 @@ async function prepareCodexAttemptRoute(resources, turnRuntime, notifications, h
5558
5998
  }
5559
5999
  return resourceState.turnRoute;
5560
6000
  };
5561
- try {
5562
- await ensureCurrentThreadRoute();
5563
- } catch (error) {
5564
- activateNativePreToolUseFailureFallback();
5565
- releaseCurrentRoute();
5566
- resourceState.nativeHookRelay?.unregister();
5567
- await releaseSandboxExecEnvironment();
5568
- releaseSharedClientLeaseOnce();
5569
- throw error;
5570
- }
6001
+ await ensureCurrentThreadRoute();
5571
6002
  return { ensureCurrentThreadRoute };
5572
6003
  }
5573
6004
  //#endregion
@@ -5690,7 +6121,7 @@ async function prepareCodexAttemptRuntime(connection) {
5690
6121
  const authenticatedScheduledMode = params.trigger === "cron" && params.scheduledToolPolicy !== void 0 && Array.isArray(params.toolsAllow);
5691
6122
  const scheduledConfiguredMcpSurface = authenticatedScheduledMode && (bundleMcpThreadConfig.staticServerNames.length > 0 || mutable.startupBinding?.configuredMcpOwnershipVersion === 1);
5692
6123
  const cronCreatorAuthorityCapability = params.cronCreatorAuthorityCapability;
5693
- const hasFreshCreatorAuthority = cronCreatorAuthorityCapability?.active === true && !(cronCreatorAuthorityCapability.controlUiAdmin && cronCreatorAuthorityCapability.callerOrigin.kind === "unknown") && cronCreatorAuthorityCapability.runId === params.runId && !cronCreatorAuthorityCapability.signal.aborted;
6124
+ const hasFreshCreatorAuthority = cronCreatorAuthorityCapability?.active === true && !(cronCreatorAuthorityCapability.managementEntitlement && cronCreatorAuthorityCapability.callerOrigin.kind === "unknown") && cronCreatorAuthorityCapability.runId === params.runId && !cronCreatorAuthorityCapability.signal.aborted;
5694
6125
  const mayResolveScheduledConfiguredMcpCreatorAuthority = !authenticatedScheduledMode && canResolveScheduledConfiguredMcpCreatorAuthority({
5695
6126
  trigger: params.trigger,
5696
6127
  connectionClass: appServer.connectionClass,
@@ -5710,25 +6141,64 @@ async function prepareCodexAttemptRuntime(connection) {
5710
6141
  });
5711
6142
  preDynamicStartupStages.mark("bundle-mcp");
5712
6143
  const sandboxExecServerEnabled = isCodexSandboxExecServerEnabled(pluginConfig, sandbox);
5713
- const nativeToolSurfaceEnabled = shouldEnableCodexAppServerNativeToolSurface(runtimeParams, sandbox, {
6144
+ let nativeToolSurfaceEnabled = shouldEnableCodexAppServerNativeToolSurface(runtimeParams, sandbox, {
5714
6145
  agentId: policyAgentId,
5715
6146
  runtimeSessionKey: sandboxSessionKey,
5716
6147
  sandboxExecServerEnabled
5717
6148
  });
6149
+ if (nativeToolSurfaceEnabled && sandbox?.enabled && sandbox.backend && params.hostCapabilities.retainSourceAuthority) {
6150
+ const client = await attemptClientFactory({
6151
+ assertCurrent: connection.assertCurrent,
6152
+ startOptions: appServer.start,
6153
+ pluginConfig,
6154
+ ...startupPreparedAuth ? { preparedAuth: startupPreparedAuth } : { authProfileId: startupClientAuthProfileId },
6155
+ authRequirement: connection.startupAuthRequirement,
6156
+ authProfileStore: attemptAuthProfileStore,
6157
+ authBindingFingerprint: preparedAuthBinding?.fingerprint,
6158
+ ...connection.runtimeArtifactRequest ? {
6159
+ runtimeArtifactMode: "capture",
6160
+ ...connection.runtimeArtifactRequest.expected ? { expectedRuntimeArtifact: connection.runtimeArtifactRequest.expected } : {}
6161
+ } : {},
6162
+ agentId: sessionAgentId,
6163
+ agentDir,
6164
+ config: params.config,
6165
+ abandonSignal: runAbortController.signal,
6166
+ timeoutMs: appServer.requestTimeoutMs
6167
+ });
6168
+ try {
6169
+ connection.assertCurrent();
6170
+ await assertCodexNativeHookRelayAllowed(client, runAbortController.signal, appServer.requestTimeoutMs);
6171
+ connection.assertCurrent();
6172
+ } catch (error) {
6173
+ if (!(error instanceof CodexManagedHooksOnlyError)) throw error;
6174
+ connection.assertCurrent();
6175
+ nativeToolSurfaceEnabled = false;
6176
+ embeddedAgentLog.info("Codex managed-only hooks require sandbox-backed OpenClaw tools");
6177
+ } finally {
6178
+ if (attemptClientFactory === createIsolatedCodexAppServerClient) await client.closeAndWait();
6179
+ else releaseLeasedSharedCodexAppServerClient(client);
6180
+ }
6181
+ }
5718
6182
  const configuredMcpSurface = scheduledConfiguredMcpSurface ? "scheduled" : !nativeToolSurfaceEnabled && bundleMcpThreadConfig.staticServerNames.length > 0 ? "transient" : void 0;
5719
6183
  preDynamicStartupStages.mark("native-tool-surface");
5720
- const nativeProviderWebSearchSupport = resolveCodexWebSearchPlan({
6184
+ const webSearchPlan = resolveCodexWebSearchPlan({
5721
6185
  config: params.config,
5722
6186
  disableTools: params.disableTools,
5723
6187
  nativeToolSurfaceEnabled
5724
- }).kind === "native-hosted" ? await resolveCodexProviderWebSearchSupport({
6188
+ });
6189
+ const supervisedSearchFingerprint = usesSupervisionConnection && !mutable.startupBinding?.pendingSupervisionBranch ? mutable.startupBinding?.webSearchThreadConfigFingerprint : void 0;
6190
+ let nativeProviderWebSearchSupport;
6191
+ if (webSearchPlan.kind !== "native-hosted" || supervisedSearchFingerprint === fingerprintJsonObject(resolveCodexWebSearchPlan({ disableTools: true }).threadConfig)) nativeProviderWebSearchSupport = "unsupported";
6192
+ else if (supervisedSearchFingerprint === fingerprintJsonObject(webSearchPlan.threadConfig)) nativeProviderWebSearchSupport = "supported";
6193
+ else nativeProviderWebSearchSupport = await resolveCodexProviderWebSearchSupport({
5725
6194
  clientFactory: attemptClientFactory,
5726
6195
  appServer,
5727
6196
  authProfileId: startupClientAuthProfileId,
5728
6197
  preparedAuth: startupPreparedAuth,
5729
6198
  agentDir,
5730
6199
  config: params.config,
5731
- modelProviderOverride: usesSupervisionConnection ? mutable.startupBinding?.modelProvider : resolveCodexAppServerThreadModelSelection({
6200
+ expectedNativeModelProvider: usesSupervisionConnection ? mutable.startupBinding?.modelProvider : void 0,
6201
+ modelProviderOverride: usesSupervisionConnection ? void 0 : resolveCodexAppServerThreadModelSelection({
5732
6202
  provider: params.provider,
5733
6203
  model: params.modelId,
5734
6204
  binding: mutable.startupBinding,
@@ -5738,7 +6208,7 @@ async function prepareCodexAttemptRuntime(connection) {
5738
6208
  config: params.config
5739
6209
  }).modelProvider,
5740
6210
  signal: runAbortController.signal
5741
- }) : "unsupported";
6211
+ });
5742
6212
  preDynamicStartupStages.mark("provider-capabilities");
5743
6213
  for (const diagnostic of bundleMcpThreadConfig.diagnostics) embeddedAgentLog.warn(`bundle-mcp: ${diagnostic.pluginId}: ${diagnostic.message}`);
5744
6214
  if (activeContextEngine) assertContextEngineHostSupport({
@@ -5868,7 +6338,7 @@ const DYNAMIC_TOOL_TERMINAL_DIAGNOSTIC_TYPES = [
5868
6338
  "tool.execution.error",
5869
6339
  "tool.execution.blocked"
5870
6340
  ];
5871
- function createCodexAttemptServerRequestController(resources, turnRuntime, lifecycle) {
6341
+ function createCodexAttemptServerRequestController(resources, turnRuntime, lifecycle, waitForNativeItems) {
5872
6342
  const { prompt, state: resourceState, projectorRef, trajectoryRecorder } = resources;
5873
6343
  const { context } = prompt;
5874
6344
  const { runtime, attemptTools } = context;
@@ -5878,7 +6348,24 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
5878
6348
  const { compactionPlanState, toolBridge, toolOutcomeOrdinals, suppressedDynamicToolOutcomeOrdinals, allocateCodexToolOutcomeOrdinal } = attemptTools;
5879
6349
  const { state, turnIdRef, userInputBridgeRef, openClawDynamicToolExecutions, pendingOpenClawDynamicToolCompletionIds, noteProgress } = turnRuntime;
5880
6350
  const { emitExecutionPhaseOnce, scheduleTurnReleaseAfterTerminalDynamicTool, scheduleTerminalDynamicToolReleaseCheck } = lifecycle;
5881
- const handleServerRequest = async (request, scope, requestSignal = new AbortController().signal) => {
6351
+ let refreshDrain;
6352
+ let refreshStopping = false;
6353
+ const settlePluginRuntimeRefresh = async (turnId) => {
6354
+ if (!params.pluginRuntimeRefreshPending?.()) return;
6355
+ refreshDrain ??= createDeferred();
6356
+ state.pluginRuntimeRefreshStop = refreshDrain.promise;
6357
+ if (!refreshStopping && pendingOpenClawDynamicToolCompletionIds.size === 0) {
6358
+ refreshStopping = true;
6359
+ state.pendingTerminalDynamicToolRelease = void 0;
6360
+ turnRuntime.steeringQueueRef.current?.cancel();
6361
+ turnRuntime.interruptTurn(turnId, { locallyCompleted: true }).then(async (confirmed) => {
6362
+ if (!confirmed) throw new Error("Plugin reload could not confirm the previous Codex turn stopped.");
6363
+ await terminateCodexBackgroundTerminals(resourceState.client, resourceState.thread.threadId, params.oneShotCliRun === true, waitForNativeItems);
6364
+ }).then(refreshDrain.resolve, refreshDrain.reject).finally(turnRuntime.completeTurn);
6365
+ }
6366
+ await refreshDrain.promise;
6367
+ };
6368
+ const handleServerRequest = async (request, scope, requestSignal = new AbortController().signal, setExecutionTimeoutMs) => {
5882
6369
  const signal = AbortSignal.any([runAbortController.signal, requestSignal]);
5883
6370
  const turnId = turnIdRef.current;
5884
6371
  const projector = projectorRef.current;
@@ -5905,17 +6392,11 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
5905
6392
  signal
5906
6393
  });
5907
6394
  if (approvalResult.kind === "handled") return approvalResult.response;
5908
- return await userInputBridgeRef.current?.handleElicitationRequest({
5909
- id: request.id,
5910
- params: request.params
5911
- });
6395
+ return await userInputBridgeRef.current?.handleElicitationRequest(request, signal);
5912
6396
  }
5913
6397
  if (request.method === "item/tool/requestUserInput") {
5914
6398
  if (scope.turnId === turnId) markCurrentTurnRequestProgress();
5915
- return await userInputBridgeRef.current?.handleRequest({
5916
- id: request.id,
5917
- params: request.params
5918
- });
6399
+ return await userInputBridgeRef.current?.handleRequest(request, signal);
5919
6400
  }
5920
6401
  if (request.method !== "item/tool/call") {
5921
6402
  if (isCodexAppServerApprovalRequest(request.method)) {
@@ -5939,7 +6420,9 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
5939
6420
  const replayedExecution = openClawDynamicToolExecutions.get(call);
5940
6421
  if (replayedExecution) {
5941
6422
  markCurrentTurnRequestProgress();
5942
- return toCodexDynamicToolProtocolResponse(await replayedExecution);
6423
+ const response = await replayedExecution;
6424
+ await settlePluginRuntimeRefresh(turnId);
6425
+ return toCodexDynamicToolProtocolResponse(response);
5943
6426
  }
5944
6427
  const toolCallOrdinal = allocateCodexToolOutcomeOrdinal?.(call.callId);
5945
6428
  markCurrentTurnRequestProgress();
@@ -5964,23 +6447,38 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
5964
6447
  const toolMeta = inferCodexDynamicToolMeta(call, resolveCodexToolProgressDetailMode(params.toolProgressDetail));
5965
6448
  const toolArgs = sanitizeCodexToolArguments(call.arguments);
5966
6449
  const commandBearing = isCodexCommandBearingToolCall(call.tool, toolArgs);
5967
- const shouldEmitDynamicToolProgress = shouldEmitTranscriptToolProgress(call.tool, toolArgs);
5968
- if (shouldEmitDynamicToolProgress) emitCodexAppServerEvent(params, {
5969
- stream: "tool",
5970
- data: {
5971
- phase: "start",
5972
- name: call.tool,
5973
- itemId: call.callId,
6450
+ const shouldEmitDynamicToolProgress = shouldEmitTranscriptToolProgress(call.tool);
6451
+ if (shouldEmitDynamicToolProgress) {
6452
+ const activity = projectAgentToolActivity({
5974
6453
  toolCallId: call.callId,
5975
- ...toolMeta ? { meta: toolMeta } : {},
5976
- ...toolArgs ? { args: toolArgs } : {},
5977
- ...commandBearing ? { commandBearing: true } : {}
5978
- }
5979
- });
6454
+ name: call.tool,
6455
+ phase: "start",
6456
+ args: toolArgs,
6457
+ meta: toolMeta
6458
+ });
6459
+ emitCodexAppServerEvent(params, {
6460
+ stream: "item",
6461
+ data: activity
6462
+ });
6463
+ emitCodexAppServerEvent(params, {
6464
+ stream: "tool",
6465
+ data: {
6466
+ phase: "start",
6467
+ name: call.tool,
6468
+ itemId: call.callId,
6469
+ toolCallId: call.callId,
6470
+ ...toolMeta ? { meta: toolMeta } : {},
6471
+ ...toolArgs ? { args: toolArgs } : {},
6472
+ ...commandBearing ? { commandBearing: true } : {}
6473
+ }
6474
+ });
6475
+ }
5980
6476
  const dynamicToolTimeoutMs = resolveDynamicToolCallTimeoutMs({
5981
6477
  call,
5982
- config: params.config
6478
+ config: params.config,
6479
+ toolBridge
5983
6480
  });
6481
+ setExecutionTimeoutMs?.(dynamicToolTimeoutMs);
5984
6482
  const toolStartedAt = Date.now();
5985
6483
  let terminalDiagnosticObserved = false;
5986
6484
  const unsubscribeToolDiagnosticObserver = onInternalDiagnosticEvent((event) => {
@@ -6057,6 +6555,14 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6057
6555
  }
6058
6556
  if (shouldEmitDynamicToolProgress) {
6059
6557
  const progressResponse = toCodexDynamicToolProgressResponse(response, protocolResponse);
6558
+ const activity = projectAgentToolActivity({
6559
+ toolCallId: call.callId,
6560
+ name: call.tool,
6561
+ phase: "result",
6562
+ args: response.executedArguments ?? call.arguments,
6563
+ result: toTranscriptToolResult(progressResponse),
6564
+ isError: !protocolResponse.success
6565
+ });
6060
6566
  emitCodexAppServerEvent(params, {
6061
6567
  stream: "tool",
6062
6568
  data: {
@@ -6070,6 +6576,10 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6070
6576
  result: toTranscriptToolResult(progressResponse)
6071
6577
  }
6072
6578
  });
6579
+ emitCodexAppServerEvent(params, {
6580
+ stream: "item",
6581
+ data: activity
6582
+ });
6073
6583
  }
6074
6584
  if (!terminalDiagnosticObserved && !hasPendingDynamicToolTerminalDiagnostic({
6075
6585
  call,
@@ -6086,7 +6596,8 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6086
6596
  durationMs: toolDurationMs
6087
6597
  });
6088
6598
  pendingOpenClawDynamicToolCompletionIds.delete(call.callId);
6089
- if (response.terminate === true && response.success) scheduleTurnReleaseAfterTerminalDynamicTool({
6599
+ if (params.pluginRuntimeRefreshPending?.()) await settlePluginRuntimeRefresh(turnId);
6600
+ else if (response.terminate === true && response.success) scheduleTurnReleaseAfterTerminalDynamicTool({
6090
6601
  call,
6091
6602
  response,
6092
6603
  durationMs: toolDurationMs
@@ -6112,6 +6623,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6112
6623
  sessionKey: params.sessionKey,
6113
6624
  durationMs: Math.max(0, Date.now() - toolStartedAt)
6114
6625
  });
6626
+ await settlePluginRuntimeRefresh(turnId);
6115
6627
  throw error;
6116
6628
  } finally {
6117
6629
  toolOutcomeOrdinals.delete(call.callId);
@@ -6131,7 +6643,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6131
6643
  //#region extensions/codex/src/app-server/attempt-runtime-artifact.ts
6132
6644
  async function verifyStartupArtifact(params) {
6133
6645
  if (!params.request) return;
6134
- const { readCodexAppServerClientRuntimeArtifact, validateCodexAppServerRuntimeArtifact } = await import("./runtime-artifact-BJ0rXBZ_.js");
6646
+ const { readCodexAppServerClientRuntimeArtifact, validateCodexAppServerRuntimeArtifact } = await import("./runtime-artifact-CLZ8udh-.mjs");
6135
6647
  const runtimeArtifact = readCodexAppServerClientRuntimeArtifact(params.client);
6136
6648
  const expected = params.request.expected;
6137
6649
  const matchesExpected = !expected || runtimeArtifact?.id === expected.id && runtimeArtifact.fingerprint === expected.fingerprint;
@@ -6154,7 +6666,7 @@ function startCodexComputerUseHealthMonitor(params) {
6154
6666
  reason: params.config.enabled ? "health_disabled" : "disabled"
6155
6667
  };
6156
6668
  }
6157
- const fingerprint = buildComputerUseHealthMonitorFingerprint(params.config);
6669
+ const fingerprint = buildComputerUseHealthMonitorFingerprint(params.config, params.tools);
6158
6670
  const intervalMs = params.config.healthCheckIntervalMinutes * 6e4;
6159
6671
  if (existing?.fingerprint === fingerprint) return {
6160
6672
  started: false,
@@ -6166,7 +6678,7 @@ function startCodexComputerUseHealthMonitor(params) {
6166
6678
  fingerprint,
6167
6679
  intervalMs,
6168
6680
  timer: setInterval(() => {
6169
- runCodexComputerUseHealthProbe(params.client, params.config, monitor);
6681
+ runCodexComputerUseHealthProbe(params.client, params.config, monitor, params.tools);
6170
6682
  }, intervalMs),
6171
6683
  disposeCloseHandler: () => void 0,
6172
6684
  running: false
@@ -6182,22 +6694,28 @@ function startCodexComputerUseHealthMonitor(params) {
6182
6694
  intervalMs
6183
6695
  };
6184
6696
  }
6185
- function buildComputerUseHealthMonitorFingerprint(config) {
6697
+ function buildComputerUseHealthMonitorFingerprint(config, tools) {
6186
6698
  return JSON.stringify({
6187
6699
  autoRepair: config.autoRepair,
6188
6700
  healthCheckIntervalMinutes: config.healthCheckIntervalMinutes,
6189
6701
  liveTestTimeoutMs: config.liveTestTimeoutMs,
6190
6702
  mcpServerName: config.mcpServerName,
6191
- toolCallTimeoutMs: config.toolCallTimeoutMs
6703
+ toolCallTimeoutMs: config.toolCallTimeoutMs,
6704
+ tools: tools?.toSorted()
6192
6705
  });
6193
6706
  }
6194
- async function runCodexComputerUseHealthProbe(client, config, monitor) {
6707
+ async function runCodexComputerUseHealthProbe(client, config, monitor, tools) {
6195
6708
  if (monitor.running) return;
6196
6709
  monitor.running = true;
6197
6710
  try {
6198
6711
  const { liveTest, repair } = await runCodexComputerUseLiveTest({
6712
+ client,
6199
6713
  config,
6200
- request: async (method, requestParams, requestOptions) => await client.request(method, requestParams, { timeoutMs: requestOptions?.timeoutMs ?? config.liveTestTimeoutMs })
6714
+ tools,
6715
+ request: async (method, requestParams, requestOptions) => await client.request(method, requestParams, {
6716
+ timeoutMs: requestOptions?.timeoutMs ?? config.liveTestTimeoutMs,
6717
+ ...requestOptions?.signal ? { signal: requestOptions.signal } : {}
6718
+ })
6201
6719
  });
6202
6720
  if (!liveTest.ok) {
6203
6721
  embeddedAgentLog.warn("codex computer-use periodic health failed", {
@@ -6399,6 +6917,7 @@ async function startCodexAttemptThread(params) {
6399
6917
  try {
6400
6918
  const attemptParams = params.buildAttemptParams();
6401
6919
  params.assertCurrent?.();
6920
+ if (startupAbandonController.signal.aborted) throw new CodexAppServerStartupError("aborted");
6402
6921
  startupClient = await params.attemptClientFactory({
6403
6922
  assertCurrent: params.assertCurrent,
6404
6923
  startOptions: params.appServer.start,
@@ -6447,15 +6966,16 @@ async function startCodexAttemptThread(params) {
6447
6966
  config: params.config
6448
6967
  });
6449
6968
  const turnRouter = getCodexAppServerTurnRouter(activeStartupClient);
6969
+ let computerUseTools = [];
6450
6970
  try {
6451
- await ensureCodexComputerUse({
6971
+ computerUseTools = (await ensureCodexComputerUse({
6452
6972
  client: activeStartupClient,
6453
6973
  pluginConfig: params.pluginConfig,
6454
6974
  config: params.config,
6455
6975
  agentDir: params.agentDir,
6456
6976
  timeoutMs: params.appServer.requestTimeoutMs,
6457
6977
  signal: startupAbandonController.signal
6458
- });
6978
+ })).tools;
6459
6979
  } catch (error) {
6460
6980
  if (startupAbandonController.signal.aborted || isCodexAppServerStartSelectionChangedError(error)) throw error;
6461
6981
  throw new AgentHarnessPreflightError(`Codex Computer Use readiness failed: ${formatErrorMessage(error)}`, {
@@ -6517,7 +7037,8 @@ async function startCodexAttemptThread(params) {
6517
7037
  appServerStartOptions: params.appServer.start,
6518
7038
  timeoutMs: params.appServer.requestTimeoutMs,
6519
7039
  signal: AbortSignal.any([params.signal, startupAbandonController.signal]),
6520
- onExecutionDisconnect: params.onExecutionDisconnect
7040
+ onExecutionDisconnect: params.onExecutionDisconnect,
7041
+ requireProcessAuthority: Boolean(attemptParams.hostCapabilities.retainSourceAuthority)
6521
7042
  }) : void 0;
6522
7043
  startupSandboxEnvironmentAcquired = Boolean(startupSandboxEnvironment);
6523
7044
  if (startupAbandonController.signal.aborted) throw new CodexAppServerStartupError("aborted");
@@ -6617,7 +7138,8 @@ async function startCodexAttemptThread(params) {
6617
7138
  startupSandboxEnvironmentAcquired = false;
6618
7139
  startCodexComputerUseHealthMonitor({
6619
7140
  client: activeStartupClient,
6620
- config: params.computerUseConfig
7141
+ config: params.computerUseConfig,
7142
+ tools: computerUseTools
6621
7143
  });
6622
7144
  startupAttemptSucceeded = true;
6623
7145
  return {
@@ -6652,7 +7174,6 @@ async function startCodexAttemptThread(params) {
6652
7174
  const sharedClient = clearSharedCodexAppServerClientIfCurrentAndUnclaimed(startupClientForAbandonedRequestCleanup);
6653
7175
  if (sharedClient.found && !sharedClient.closed) startupClientForAbandonedRequestCleanup = void 0;
6654
7176
  }
6655
- throw error;
6656
7177
  } finally {
6657
7178
  if (!startupAttemptSucceeded) {
6658
7179
  if (releaseSharedClientLease === startupClientLease) releaseSharedClientLease = void 0;
@@ -6663,13 +7184,20 @@ async function startCodexAttemptThread(params) {
6663
7184
  }
6664
7185
  }
6665
7186
  }
7187
+ if (startupAttemptError instanceof CodexThreadClientReplacementError && startupClient) {
7188
+ if (!(await startupClient.closeAndWait()).exited) throw new AgentHarnessPreflightError("The previous conversation process did not confirm shutdown; the conversation was preserved.");
7189
+ }
7190
+ throw startupAttemptError;
6666
7191
  };
7192
+ let replacedSettledFailureClient = false;
6667
7193
  for (let attempt = 1; attempt <= CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS; attempt += 1) try {
6668
7194
  return await startupAttempt();
6669
7195
  } catch (error) {
6670
7196
  const selectionChanged = isCodexAppServerStartSelectionChangedError(error);
6671
- if (startupAbandonController.signal.aborted || !selectionChanged && !isCodexAppServerConnectionClosedError(error)) throw error;
6672
- const refreshedSharedClient = selectionChanged ? retireSharedCodexAppServerClientIfCurrent(attemptedClient) : clearSharedCodexAppServerClientIfCurrent(attemptedClient);
7197
+ const clientRetired = error instanceof CodexThreadClientReplacementError;
7198
+ if (startupAbandonController.signal.aborted || clientRetired && replacedSettledFailureClient || !clientRetired && !selectionChanged && !isCodexAppServerConnectionClosedError(error)) throw error;
7199
+ replacedSettledFailureClient ||= clientRetired;
7200
+ const refreshedSharedClient = clientRetired ? void 0 : selectionChanged ? retireSharedCodexAppServerClientIfCurrent(attemptedClient) : clearSharedCodexAppServerClientIfCurrent(attemptedClient);
6673
7201
  if (startupClientForAbandonedRequestCleanup === attemptedClient) startupClientForAbandonedRequestCleanup = void 0;
6674
7202
  if (attempt >= CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS) {
6675
7203
  embeddedAgentLog.warn(selectionChanged ? "codex app-server executable selection kept changing during startup; retries exhausted" : "codex app-server connection closed during startup; retries exhausted", {
@@ -6680,8 +7208,8 @@ async function startCodexAttemptThread(params) {
6680
7208
  });
6681
7209
  throw error;
6682
7210
  }
6683
- const retryDelayMs = selectionChanged ? 0 : 1e3 * 2 ** (attempt - 1);
6684
- embeddedAgentLog.warn(selectionChanged ? "codex app-server executable selection changed during startup; restarting app-server and retrying" : "codex app-server connection closed during startup; restarting app-server and retrying", {
7211
+ const retryDelayMs = selectionChanged || clientRetired ? 0 : 1e3 * 2 ** (attempt - 1);
7212
+ embeddedAgentLog.warn(clientRetired ? "codex app-server retired a settled failed client; resuming on a fresh client" : selectionChanged ? "codex app-server executable selection changed during startup; restarting app-server and retrying" : "codex app-server connection closed during startup; restarting app-server and retrying", {
6685
7213
  attempt,
6686
7214
  nextAttempt: attempt + 1,
6687
7215
  maxAttempts: CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS,
@@ -6712,14 +7240,10 @@ async function startCodexAttemptThread(params) {
6712
7240
  params.signal.removeEventListener("abort", abandonStartupAcquire);
6713
7241
  }
6714
7242
  }
6715
- function shouldRetireCodexStartupClient(error, spawnedBy, signal) {
6716
- if (signal.aborted || isCodexAppServerStartupError(error) || isCodexAppServerRequestTimeoutError(error)) return true;
6717
- return !isCodexAppServerStartSelectionChangedError(error) && !isCodexAppServerOverloadError(error) && !(error instanceof AgentHarnessPreflightError && error.scope === void 0) && (isCodexAppServerBrokenPipeError(error) || !spawnedBy);
6718
- }
6719
7243
  //#endregion
6720
7244
  //#region extensions/codex/src/app-server/run-attempt-start.ts
6721
7245
  async function startCodexAttemptRuntime(resources) {
6722
- const { prompt, state, trajectoryRecorder, activateNativePreToolUseFailureFallback, releaseSandboxExecEnvironment, releaseSharedClientLeaseAndRetireOneShotClient, releaseCurrentRoute, runCleanupStep, startupTimeoutMs, buildNativeHookRelayFinalConfigPatch } = resources;
7246
+ const { prompt, state, trajectoryRecorder, startupTimeoutMs, buildNativeHookRelayFinalConfigPatch } = resources;
6723
7247
  const { context, turnState, rebuildCodexTurnPromptTextFromCurrentProjection, applyNoContextEngineContinuityProjection } = prompt;
6724
7248
  const { runtime, attemptTools, promptState } = context;
6725
7249
  const { connection, runtimeParams, preparedAuthBinding, buildActiveRunAttemptParams, startupAuthAccountCacheKey, startupEnvApiKeyCacheKey, bundleMcpThreadConfig, nativeToolSurfaceEnabled, nativeProviderWebSearchSupport, effectiveRuntimeModelId, sandboxExecServerEnabled } = runtime;
@@ -6763,7 +7287,7 @@ async function startCodexAttemptRuntime(resources) {
6763
7287
  developerInstructions,
6764
7288
  agentWorkspaceDeveloperInstructions: context.agentWorkspaceDeveloperInstructions,
6765
7289
  buildFinalConfigPatch: buildNativeHookRelayFinalConfigPatch,
6766
- nativeHookRelayRequired: connection.options.nativeHookRelay?.enabled !== false && params.pluginHarnessToolPolicyRestricted !== true && connection.nativeHookRelayEvents.includes("pre_tool_use") && (hasBeforeToolCallPolicy() || appServer.loopDetectionPreToolUseRelay && Boolean(connection.sandboxSessionKey) && loopDetectionEnabled),
7290
+ nativeHookRelayRequired: Boolean(resources.nativeProcessAuthority && nativeToolSurfaceEnabled) || connection.options.nativeHookRelay?.enabled !== false && params.pluginHarnessToolPolicyRestricted !== true && connection.nativeHookRelayEvents.includes("pre_tool_use") && (hasBeforeToolCallPolicy() || appServer.loopDetectionPreToolUseRelay && Boolean(connection.sandboxSessionKey) && loopDetectionEnabled),
6767
7291
  bundleMcpThreadConfig,
6768
7292
  configuredMcpDynamicSurface: attemptTools.configuredMcp !== void 0,
6769
7293
  configuredMcpOwnershipVersion: attemptTools.configuredMcpOwnershipVersion,
@@ -6803,10 +7327,11 @@ async function startCodexAttemptRuntime(resources) {
6803
7327
  if (usesSupervisionConnection && (state.thread.connectionScope !== "supervision" || state.thread.supervisionSourceThreadId !== connection.mutable.startupBinding?.supervisionSourceThreadId)) throw new Error("Codex supervised thread lost its private connection ownership");
6804
7328
  if (state.thread.lifecycle.action === "started" || state.thread.lifecycle.action === "forked") {
6805
7329
  const activePolicy = resolveReviewerPolicyContext(state.thread);
6806
- const activeConfig = resolveRuntimeOptionsForCurrentBinding({
7330
+ const activeConfig = await resolveRuntimeOptionsForCurrentBinding({
6807
7331
  modelProvider: activePolicy.modelProvider,
6808
7332
  model: activePolicy.model
6809
7333
  });
7334
+ connection.assertCurrent();
6810
7335
  const activeAppServer = resolveCodexAppServerForModelProvider({
6811
7336
  appServer: activeConfig,
6812
7337
  provider: activePolicy.modelProvider,
@@ -6847,14 +7372,8 @@ async function startCodexAttemptRuntime(resources) {
6847
7372
  toolCount: flattenCodexDynamicToolFunctions(toolBridge.specs).length
6848
7373
  });
6849
7374
  connection.mutable.pluginAppServer = pluginAppServer;
7375
+ await resources.registerNativeSubagentMonitor(state.thread.threadId);
6850
7376
  } catch (error) {
6851
- await runCleanupStep("codex-start-failure-hook-fallback", activateNativePreToolUseFailureFallback);
6852
- await runCleanupStep("codex-start-failure-route-release", releaseCurrentRoute);
6853
- const nativeHookRelay = state.nativeHookRelay;
6854
- state.nativeHookRelay = void 0;
6855
- await runCleanupStep("codex-start-failure-native-hook-relay", () => nativeHookRelay?.unregister());
6856
- await runCleanupStep("codex-start-failure-sandbox-release", releaseSandboxExecEnvironment);
6857
- await runCleanupStep("codex-start-failure-shared-client-release", releaseSharedClientLeaseAndRetireOneShotClient);
6858
7377
  throw error instanceof CodexThreadPolicyHandoffError ? error : state.executionDisconnectError ?? error;
6859
7378
  }
6860
7379
  }
@@ -7005,93 +7524,103 @@ async function prepareCodexAttemptTools(runtime) {
7005
7524
  }
7006
7525
  }
7007
7526
  let requireExplicitMessageTarget;
7008
- const tools = await buildDynamicTools({
7009
- ...commonToolParams,
7010
- registerRunCleanup: (cleanup) => runCleanups.push(cleanup),
7011
- onMessageToolTargetResolved: (required) => {
7012
- requireExplicitMessageTarget = required;
7013
- },
7014
- cronCreatorToolAllowlistRef: cronCreatorToolAllowlist,
7015
- cronCreatorToolAllowlistCaptureRef,
7016
- onPersistentWebSearchPolicyResolved: (allowed) => {
7017
- toolState.persistentWebSearchAllowed = allowed;
7018
- },
7019
- onWebSearchPolicyResolved: (allowed) => {
7020
- toolState.webSearchAllowed = allowed;
7021
- }
7022
- });
7023
- const registeredTools = nativeSpecs ? [] : await buildDynamicTools({
7024
- ...commonToolParams,
7025
- forceHeartbeatTool: true,
7026
- ignoreDisableMessageTool: true,
7027
- ignoreRuntimePlan: true
7028
- });
7029
- const policyContext = {
7030
- config: params.config,
7031
- sessionKey: sandboxSessionKey,
7032
- runSessionKey: params.sessionKey && params.sessionKey !== sandboxSessionKey ? params.sessionKey : void 0,
7033
- sessionId: params.sessionId,
7034
- runId: params.runId,
7035
- agentId: policyAgentId,
7036
- agentDir: agentDir ?? resolveAgentDir$2(params.config ?? {}, sessionAgentId),
7037
- agentAccountId: params.agentAccountId,
7038
- messageProvider: params.messageProvider ?? params.messageChannel,
7039
- messageChannel: params.messageChannel,
7040
- chatType: params.chatType,
7041
- messageTo: params.messageTo,
7042
- messageThreadId: params.messageThreadId,
7043
- currentChannelId: params.currentChannelId,
7044
- currentMessagingTarget: params.currentMessagingTarget,
7045
- currentThreadTs: params.currentThreadTs,
7046
- currentMessageId: params.currentMessageId,
7047
- groupId: params.groupId,
7048
- groupChannel: params.groupChannel,
7049
- groupSpace: params.groupSpace,
7050
- memberRoleIds: params.memberRoleIds,
7051
- spawnedBy: params.spawnedBy,
7052
- senderId: params.senderId,
7053
- senderName: params.senderName,
7054
- senderUsername: params.senderUsername,
7055
- senderE164: params.senderE164,
7056
- senderIsOwner: params.senderIsOwner,
7057
- modelProvider: params.provider,
7058
- modelId: params.modelId,
7059
- modelApi: params.model.api,
7060
- modelContextWindowTokens: params.model.contextWindow,
7061
- modelHasVision: params.model.input?.includes("image") ?? false,
7062
- workspaceDir: effectiveWorkspace,
7063
- cwd: effectiveCwd ?? effectiveWorkspace,
7064
- sandboxToolPolicy: sandbox?.tools,
7065
- conversationToolPolicy: params.conversationToolPolicy,
7066
- inputProvenance: params.inputProvenance,
7067
- trustedInternalHandoff: params.trustedInternalHandoff,
7068
- scheduledToolPolicy: params.scheduledToolPolicy
7069
- };
7070
- const reservedToolNames = [...tools.map((tool) => tool.name), ...registeredTools.map((tool) => tool.name)];
7071
- const turnSourceChannel = params.messageChannel ?? params.messageProvider;
7072
- const turnSourceTo = params.currentMessagingTarget ?? params.currentChannelId;
7073
- const requester = {
7074
- ...turnSourceChannel ? { channel: turnSourceChannel } : {},
7075
- ...params.agentAccountId ? { accountId: params.agentAccountId } : {},
7076
- ...params.senderId ? { senderId: params.senderId } : {},
7077
- ...params.senderIsOwner !== void 0 ? { senderIsOwner: params.senderIsOwner } : {},
7078
- ...params.memberRoleIds?.length ? { roleIds: [...params.memberRoleIds] } : {}
7079
- };
7080
- const hasRequester = Object.keys(requester).length > 0;
7081
- const configuredMcp = configuredMcpSurface ? await materializeStaticMcpToolsForHarnessRun({
7082
- sessionId: params.sessionId,
7083
- sessionKey: params.sessionKey,
7084
- agentId: sessionAgentId,
7085
- workspaceDir: effectiveWorkspace,
7086
- agentDir: policyContext.agentDir,
7087
- cfg: params.config,
7088
- manifestRegistry: bundleManifestRegistry,
7089
- reservedToolNames,
7090
- toolsAllow: params.toolsAllow,
7091
- toolOverrides: codexMcpToolOverrides,
7092
- autoApproveCodexAppServerApprovals: shouldAutoApproveCodexAppServerApprovals(connection.appServer),
7093
- projectedMcpServers: bundleMcpThreadConfig.configPatch?.mcp_servers,
7094
- ...configuredMcpSurface === "transient" ? { requestInteractiveCodexApproval: async (approval) => {
7527
+ let configuredMcp;
7528
+ let scopedMcpTools;
7529
+ let toolDisposal;
7530
+ const disposeTools = (reason) => toolDisposal ??= (async () => {
7531
+ await runAgentCleanupStep({
7532
+ runId: params.runId,
7533
+ sessionId: params.sessionId,
7534
+ step: "codex-dynamic-tool-cleanup",
7535
+ log: embeddedAgentLog,
7536
+ cleanup: async () => {
7537
+ const errors = (await Promise.allSettled(runCleanups.splice(0).map(async (cleanup) => await cleanup(reason)))).flatMap((result) => result.status === "rejected" ? [result.reason] : []);
7538
+ if (params.oneShotCliRun && errors.length) throw new AggregateError(errors, "Codex tool cleanup failed");
7539
+ }
7540
+ });
7541
+ for (const [step, materialized] of [["codex-scoped-mcp-dispose", scopedMcpTools], ["codex-configured-mcp-dispose", configuredMcp]]) await runAgentCleanupStep({
7542
+ runId: params.runId,
7543
+ sessionId: params.sessionId,
7544
+ step,
7545
+ log: embeddedAgentLog,
7546
+ cleanup: async () => materialized?.dispose()
7547
+ });
7548
+ })();
7549
+ try {
7550
+ const tools = await buildDynamicTools({
7551
+ ...commonToolParams,
7552
+ registerRunCleanup: (cleanup) => runCleanups.push(cleanup),
7553
+ onMessageToolTargetResolved: (required) => {
7554
+ requireExplicitMessageTarget = required;
7555
+ },
7556
+ cronCreatorToolAllowlistRef: cronCreatorToolAllowlist,
7557
+ cronCreatorToolAllowlistCaptureRef,
7558
+ onPersistentWebSearchPolicyResolved: (allowed) => {
7559
+ toolState.persistentWebSearchAllowed = allowed;
7560
+ },
7561
+ onWebSearchPolicyResolved: (allowed) => {
7562
+ toolState.webSearchAllowed = allowed;
7563
+ }
7564
+ });
7565
+ const registeredTools = nativeSpecs ? [] : await buildDynamicTools({
7566
+ ...commonToolParams,
7567
+ forceHeartbeatTool: true,
7568
+ ignoreDisableMessageTool: true,
7569
+ ignoreRuntimePlan: true
7570
+ });
7571
+ const policyContext = {
7572
+ config: params.config,
7573
+ sessionKey: sandboxSessionKey,
7574
+ runSessionKey: params.sessionKey && params.sessionKey !== sandboxSessionKey ? params.sessionKey : void 0,
7575
+ sessionId: params.sessionId,
7576
+ runId: params.runId,
7577
+ agentId: policyAgentId,
7578
+ agentDir: agentDir ?? resolveAgentDir$2(params.config ?? {}, sessionAgentId),
7579
+ agentAccountId: params.agentAccountId,
7580
+ messageProvider: params.messageProvider ?? params.messageChannel,
7581
+ messageChannel: params.messageChannel,
7582
+ chatType: params.chatType,
7583
+ messageTo: params.messageTo,
7584
+ messageThreadId: params.messageThreadId,
7585
+ currentChannelId: params.currentChannelId,
7586
+ currentMessagingTarget: params.currentMessagingTarget,
7587
+ currentThreadTs: params.currentThreadTs,
7588
+ currentMessageId: params.currentMessageId,
7589
+ groupId: params.groupId,
7590
+ groupChannel: params.groupChannel,
7591
+ groupSpace: params.groupSpace,
7592
+ memberRoleIds: params.memberRoleIds,
7593
+ spawnedBy: params.spawnedBy,
7594
+ senderId: params.senderId,
7595
+ senderName: params.senderName,
7596
+ senderUsername: params.senderUsername,
7597
+ senderE164: params.senderE164,
7598
+ senderIsOwner: params.senderIsOwner,
7599
+ modelProvider: params.provider,
7600
+ modelId: params.modelId,
7601
+ modelApi: params.model.api,
7602
+ modelContextWindowTokens: params.model.contextWindow,
7603
+ modelHasVision: params.model.input?.includes("image") ?? false,
7604
+ workspaceDir: effectiveWorkspace,
7605
+ cwd: effectiveCwd ?? effectiveWorkspace,
7606
+ sandboxToolPolicy: sandbox?.tools,
7607
+ conversationToolPolicy: params.conversationToolPolicy,
7608
+ inputProvenance: params.inputProvenance,
7609
+ trustedInternalHandoff: params.trustedInternalHandoff,
7610
+ scheduledToolPolicy: params.scheduledToolPolicy
7611
+ };
7612
+ const reservedToolNames = [...tools.map((tool) => tool.name), ...registeredTools.map((tool) => tool.name)];
7613
+ const turnSourceChannel = params.messageChannel ?? params.messageProvider;
7614
+ const turnSourceTo = params.currentMessagingTarget ?? params.currentChannelId;
7615
+ const requester = {
7616
+ ...turnSourceChannel ? { channel: turnSourceChannel } : {},
7617
+ ...params.agentAccountId ? { accountId: params.agentAccountId } : {},
7618
+ ...params.senderId ? { senderId: params.senderId } : {},
7619
+ ...params.senderIsOwner !== void 0 ? { senderIsOwner: params.senderIsOwner } : {},
7620
+ ...params.memberRoleIds?.length ? { roleIds: [...params.memberRoleIds] } : {}
7621
+ };
7622
+ const hasRequester = Object.keys(requester).length > 0;
7623
+ const requestInteractiveMcpApproval = async (approval) => {
7095
7624
  const allowedDecisions = approval.mode === "prompt" ? ["allow-once", "deny"] : [
7096
7625
  "allow-once",
7097
7626
  "allow-always",
@@ -7112,21 +7641,41 @@ async function prepareCodexAttemptTools(runtime) {
7112
7641
  isMcpToolApprovalActive: approval.isActive
7113
7642
  });
7114
7643
  if (outcome !== "approved-once" && outcome !== "approved-session") throw new Error(`${approval.serverName}/${approval.toolName}: interactive Codex approval (${approval.mode}) was not granted: ${outcome}`);
7115
- } } : {},
7116
- policyContext,
7117
- warn: (message) => embeddedAgentLog.warn(message)
7118
- }) : void 0;
7119
- let scopedMcpTools = void 0;
7120
- const disposeMcpTools = async () => {
7121
- for (const [step, materialized] of [["codex-scoped-mcp-dispose", scopedMcpTools], ["codex-configured-mcp-dispose", configuredMcp]]) await runAgentCleanupStep({
7122
- runId: params.runId,
7644
+ };
7645
+ const requestRequesterMcpApproval = async (approval) => {
7646
+ const outcome = await requestPluginApprovalOutcome({
7647
+ hostCapabilities: params.hostCapabilities,
7648
+ signal: approval.signal,
7649
+ title: `Run MCP tool ${approval.serverName}/${approval.toolName}`,
7650
+ description: `Codex approval mode "${approval.mode}" requires an operator decision before this MCP tool runs. ${formatMcpCodexApprovalRemedy(approval.serverName)}`,
7651
+ allowedDecisions: ["allow-once", "deny"],
7652
+ toolName: approval.safeToolName,
7653
+ toolCallId: approval.toolCallId,
7654
+ mcpTool: {
7655
+ server: approval.serverName,
7656
+ tool: approval.toolName
7657
+ },
7658
+ isMcpToolApprovalActive: approval.isActive
7659
+ });
7660
+ if (outcome !== "approved-once" && outcome !== "approved-session") throw new Error(`${approval.serverName}/${approval.toolName}: interactive Codex approval (${approval.mode}) was not granted: ${outcome}`);
7661
+ };
7662
+ configuredMcp = configuredMcpSurface ? await materializeStaticMcpToolsForHarnessRun({
7123
7663
  sessionId: params.sessionId,
7124
- step,
7125
- log: embeddedAgentLog,
7126
- cleanup: async () => materialized?.dispose()
7127
- });
7128
- };
7129
- try {
7664
+ sessionKey: params.sessionKey,
7665
+ agentId: sessionAgentId,
7666
+ workspaceDir: effectiveWorkspace,
7667
+ agentDir: policyContext.agentDir,
7668
+ cfg: params.config,
7669
+ manifestRegistry: bundleManifestRegistry,
7670
+ reservedToolNames,
7671
+ toolsAllow: params.toolsAllow,
7672
+ toolOverrides: codexMcpToolOverrides,
7673
+ autoApproveCodexAppServerApprovals: shouldAutoApproveCodexAppServerApprovals(connection.appServer),
7674
+ projectedMcpServers: bundleMcpThreadConfig.configPatch?.mcp_servers,
7675
+ ...configuredMcpSurface === "transient" ? { requestInteractiveCodexApproval: requestInteractiveMcpApproval } : {},
7676
+ policyContext,
7677
+ warn: (message) => embeddedAgentLog.warn(message)
7678
+ }) : void 0;
7130
7679
  scopedMcpTools = authenticatedScheduledMode ? void 0 : await materializeRequesterScopedMcpToolsForHarnessRun({
7131
7680
  sessionId: params.sessionId,
7132
7681
  sessionKey: params.sessionKey,
@@ -7135,6 +7684,8 @@ async function prepareCodexAttemptTools(runtime) {
7135
7684
  cfg: params.config,
7136
7685
  manifestRegistry: bundleManifestRegistry,
7137
7686
  toolOverrides: codexMcpToolOverrides,
7687
+ autoApproveCodexAppServerApprovals: shouldAutoApproveCodexAppServerApprovals(connection.appServer),
7688
+ requestInteractiveCodexApproval: requestRequesterMcpApproval,
7138
7689
  requesterSenderId: params.senderId,
7139
7690
  agentAccountId: params.agentAccountId,
7140
7691
  messageChannel: params.messageChannel ?? params.messageProvider,
@@ -7212,7 +7763,7 @@ async function prepareCodexAttemptTools(runtime) {
7212
7763
  agentDir: policyContext.agentDir,
7213
7764
  cfg: params.config,
7214
7765
  manifestRegistry: bundleManifestRegistry,
7215
- reservedToolNames: toolBridge.availableTools.map((tool) => tool.name),
7766
+ reservedToolNames: configuredMcp ? reservedToolNames : toolBridge.availableTools.map((tool) => tool.name),
7216
7767
  toolsAllow: params.toolsAllow,
7217
7768
  toolOverrides: codexMcpToolOverrides,
7218
7769
  autoApproveCodexAppServerApprovals: shouldAutoApproveCodexAppServerApprovals(connection.appServer),
@@ -7251,14 +7802,13 @@ async function prepareCodexAttemptTools(runtime) {
7251
7802
  requireExplicitMessageTarget,
7252
7803
  scopedMcpTools,
7253
7804
  configuredMcp,
7254
- disposeMcpTools,
7805
+ disposeTools,
7255
7806
  configuredMcpOwnershipVersion: configuredMcpSurface === "scheduled" ? 1 : void 0,
7256
7807
  captureCronCreatorToolAllowlist,
7257
7808
  scheduledAppAuthoritySourceRef,
7258
7809
  dynamicToolParams,
7259
7810
  compactionPlanState,
7260
7811
  computerContextEpoch,
7261
- runCleanups,
7262
7812
  toolBridge,
7263
7813
  toolState,
7264
7814
  toolOutcomeOrdinals,
@@ -7268,7 +7818,7 @@ async function prepareCodexAttemptTools(runtime) {
7268
7818
  runtimeYieldCompletionClaim
7269
7819
  };
7270
7820
  } catch (error) {
7271
- await disposeMcpTools();
7821
+ await disposeTools("error");
7272
7822
  throw error;
7273
7823
  }
7274
7824
  }
@@ -7302,10 +7852,102 @@ async function resolveCodexExplicitSkillInputs(params) {
7302
7852
  }
7303
7853
  }
7304
7854
  //#endregion
7855
+ //#region extensions/codex/src/app-server/provider-review-continuation.ts
7856
+ /** Reconcile the acknowledged failure with the native thread immediately before turn/start. */
7857
+ async function prepareCodexProviderReviewContinuation(params) {
7858
+ const acknowledgment = params.acknowledgment;
7859
+ if (!acknowledgment) return;
7860
+ let nativeChanged = false;
7861
+ let dispatched = false;
7862
+ let dispose = () => {};
7863
+ const assertCurrent = () => {
7864
+ params.signal.throwIfAborted();
7865
+ params.assertCurrent();
7866
+ acknowledgment.read();
7867
+ if (nativeChanged) throw new Error("The native provider review changed before dispatch");
7868
+ };
7869
+ assertCurrent();
7870
+ const snapshot = acknowledgment.read();
7871
+ if (snapshot.phase === "accepted") throw new Error("Provider review continuation cannot start another native turn");
7872
+ const assertWorkStart = () => acknowledgment.assertRuntime({
7873
+ provider: params.provider,
7874
+ model: params.model,
7875
+ runtimeId: "codex",
7876
+ api: params.api,
7877
+ assertCurrent
7878
+ });
7879
+ await assertWorkStart();
7880
+ assertCurrent();
7881
+ const { review } = snapshot;
7882
+ if (review.nativeThreadId !== params.turnStartParams.threadId || !review.review?.continuation) throw new Error("Provider review no longer matches the native thread");
7883
+ dispose = params.client.addNotificationHandler((notification) => {
7884
+ const event = notification.params;
7885
+ if (!isJsonObject(event) || event.threadId !== review.nativeThreadId) return;
7886
+ if (notification.method === "turn/started") nativeChanged = true;
7887
+ else if (notification.method === "turn/completed" || notification.method === "error" && event.willRetry !== true) {
7888
+ const turn = isJsonObject(event.turn) ? event.turn : void 0;
7889
+ const turnId = turn?.id ?? event.turnId;
7890
+ const error = isJsonObject(turn?.error) ? turn.error : isJsonObject(event.error) ? event.error : void 0;
7891
+ const failure = readCodexProviderRefusal(typeof error?.message === "string" ? error.message : void 0, error?.codexErrorInfo, { misalignment: error?.misalignment });
7892
+ if (turnId !== review.nativeTurnId || turn && turn.status !== "failed" || failure?.category !== "misalignment" || error?.misalignment != null && !isDeepStrictEqual(failure.review, review.review)) nativeChanged = true;
7893
+ }
7894
+ });
7895
+ try {
7896
+ const page = await params.client.request("thread/turns/list", {
7897
+ threadId: params.turnStartParams.threadId,
7898
+ limit: 1,
7899
+ sortDirection: "desc",
7900
+ itemsView: "notLoaded"
7901
+ }, {
7902
+ signal: params.signal,
7903
+ timeoutMs: params.timeoutMs,
7904
+ assertCurrent
7905
+ });
7906
+ assertCurrent();
7907
+ const latest = page.data[0];
7908
+ const refusal = latest && readCodexProviderRefusal(latest.error?.message, latest.error?.codexErrorInfo, { misalignment: latest.error?.misalignment });
7909
+ if (page.data.length !== 1 || !latest || latest.id !== review.nativeTurnId || latest.threadId !== void 0 && latest.threadId !== params.turnStartParams.threadId || latest.status !== "failed" || refusal?.category !== "misalignment" || latest.error?.misalignment != null && !isDeepStrictEqual(refusal.review, review.review)) throw new Error("The native provider review changed; refresh the findings before continuing");
7910
+ await assertWorkStart();
7911
+ assertCurrent();
7912
+ const message = review.review.continuation.message;
7913
+ params.turnStartParams.input = [{
7914
+ type: "text",
7915
+ text: message,
7916
+ text_elements: []
7917
+ }];
7918
+ params.turnStartParams.responsesapiClientMetadata = {
7919
+ ...params.turnStartParams.responsesapiClientMetadata,
7920
+ misalignment_override: JSON.stringify({ timestamp: Date.now() })
7921
+ };
7922
+ return {
7923
+ dispose,
7924
+ dispatch: () => {
7925
+ assertCurrent();
7926
+ if (dispatched) throw new Error("Provider review continuation already dispatched");
7927
+ dispatched = true;
7928
+ dispose();
7929
+ },
7930
+ accept: async (turnId) => {
7931
+ assertCurrent();
7932
+ if (!dispatched) throw new Error("Provider review continuation has not dispatched");
7933
+ await acknowledgment.acceptNativeTurn({
7934
+ nativeThreadId: params.turnStartParams.threadId,
7935
+ nativeTurnId: turnId,
7936
+ assertCurrent
7937
+ });
7938
+ assertCurrent();
7939
+ }
7940
+ };
7941
+ } catch (error) {
7942
+ dispose();
7943
+ throw error;
7944
+ }
7945
+ }
7946
+ //#endregion
7305
7947
  //#region extensions/codex/src/app-server/run-attempt-turn-request.ts
7306
7948
  async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurrentThreadRoute, waitForActiveNativeTurnCompletion) {
7307
7949
  const { prompt, state: resourceState, releaseCurrentRoute } = resources;
7308
- const { context, turnState, buildRenderedCodexDeveloperInstructions } = prompt;
7950
+ const { context, turnState, buildRenderedCodexDeveloperInstructions, nativeHistoryProvenancePrefix } = prompt;
7309
7951
  const { runtime, attemptTools, hookContextWindowFields, workspaceBootstrapContext } = context;
7310
7952
  const { connection, runtimeParams, effectiveRuntimeProviderId, effectiveRuntimeModelId } = runtime;
7311
7953
  const { tools, toolBridge } = attemptTools;
@@ -7359,19 +8001,25 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
7359
8001
  const startCodexTurn = async () => {
7360
8002
  const activeTurnRoute = await ensureCurrentThreadRoute();
7361
8003
  assertCodexSessionRuntimeOwnership(resourceState.thread, params.expectedSessionRuntimeOwnership);
8004
+ const selectedThread = resourceState.thread;
8005
+ const { threadId, liveThreadOwnership, model, modelProvider } = selectedThread;
8006
+ const turnClient = resourceState.client;
8007
+ const assertTurnCurrent = () => {
8008
+ connection.assertCurrent();
8009
+ liveThreadOwnership?.assertCurrent();
8010
+ if (resourceState.thread !== selectedThread || selectedThread.threadId !== threadId || selectedThread.liveThreadOwnership !== liveThreadOwnership || selectedThread.model !== model || selectedThread.modelProvider !== modelProvider) throw new Error("Codex native model or thread ownership changed during turn start.");
8011
+ };
7362
8012
  const turnAppServer = withCodexAppServerFastModeServiceTier(connection.mutable.pluginAppServer, runtimeParams);
7363
8013
  connection.mutable.pluginAppServer = turnAppServer;
7364
8014
  const references = prepareWorkspaceReferences();
7365
- const referencesRetained = turnState.codexTurnPromptText.includes(workspaceBootstrapContext.promptContext ?? "");
7366
8015
  const inferenceRoute = usesSupervisionConnection ? void 0 : getCodexInferenceThread(resourceState.client, resourceState.thread.threadId);
7367
- const turnStartParams = buildTurnStartParams({
7368
- ...runtimeParams,
7369
- images: [...prompt.contextImages, ...runtimeParams.images ?? []]
7370
- }, {
8016
+ const turnStartParams = buildTurnStartParams(runtimeParams, {
7371
8017
  threadId: resourceState.thread.threadId,
7372
8018
  cwd: resourceState.codexExecutionCwd,
7373
8019
  appServer: turnAppServer,
7374
8020
  promptText: turnState.codexTurnPromptText,
8021
+ historyProvenancePrefix: nativeHistoryProvenancePrefix,
8022
+ contextImageGroups: prompt.contextImageGroups,
7375
8023
  explicitSkillInputs,
7376
8024
  sandboxPolicy: resourceState.codexSandboxPolicy,
7377
8025
  environmentSelection: resourceState.codexEnvironmentSelection,
@@ -7427,6 +8075,17 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
7427
8075
  truncated: null
7428
8076
  } : file);
7429
8077
  }
8078
+ const continuation = await prepareCodexProviderReviewContinuation({
8079
+ acknowledgment: params.providerReviewAcknowledgment,
8080
+ client: resourceState.client,
8081
+ turnStartParams,
8082
+ provider: params.provider,
8083
+ model: params.modelId,
8084
+ api: runtimeParams.model.api,
8085
+ signal: runAbortController.signal,
8086
+ timeoutMs: params.timeoutMs,
8087
+ assertCurrent: assertTurnCurrent
8088
+ });
7430
8089
  codexModelCallDiagnostics.setRequestPayloadBytes(utf8JsonByteLength(turnStartParams));
7431
8090
  recordCodexTrajectoryContext(resources.trajectoryRecorder, {
7432
8091
  attempt: params,
@@ -7450,29 +8109,41 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
7450
8109
  }
7451
8110
  });
7452
8111
  let acceptedTurnId;
8112
+ const upstreamUserText = turnStartParams.input.flatMap((item) => item.type === "text" ? [item.text] : []).join("\n");
7453
8113
  try {
7454
- const startedTurn = assertCodexTurnStartResponse(await resourceState.client.request("turn/start", turnStartParams, {
8114
+ const startedTurn = assertCodexTurnStartResponse(await turnClient.request("turn/start", turnStartParams, {
7455
8115
  timeoutMs: params.timeoutMs,
7456
8116
  signal: runAbortController.signal,
7457
- assertCurrent: connection.assertCurrent
8117
+ assertCurrent: () => {
8118
+ assertTurnCurrent();
8119
+ continuation?.dispatch();
8120
+ }
7458
8121
  }));
7459
8122
  acceptedTurnId = startedTurn.turn.id;
7460
- connection.assertCurrent();
7461
- if (referencesRetained) references.accepted();
8123
+ resources.nativeProcessAuthority?.bindTurn(turnClient, threadId, acceptedTurnId);
8124
+ assertTurnCurrent();
8125
+ if (upstreamUserText.includes(workspaceBootstrapContext.promptContext ?? "")) references.accepted();
7462
8126
  throwIfTurnStartAcceptedAfterAbort();
7463
- return startedTurn;
8127
+ await continuation?.accept(acceptedTurnId);
8128
+ return {
8129
+ turn: startedTurn,
8130
+ upstreamUserText
8131
+ };
7464
8132
  } catch (error) {
7465
8133
  if (acceptedTurnId || isCodexAppServerIndeterminateRequestCancellationError(error)) try {
7466
- resourceState.startupClientUnsafe = !await interruptCodexTurnAndWaitBestEffort(resourceState.client, {
7467
- threadId: resourceState.thread.threadId,
8134
+ resourceState.startupClientUnsafe = !await interruptCodexTurnAndWaitBestEffort(turnClient, {
8135
+ threadId,
7468
8136
  turnId: acceptedTurnId ?? ""
7469
8137
  });
7470
- if (resourceState.startupClientUnsafe) await retireUnsafeCodexTurnClientBestEffort(resourceState.client, "startup interrupt");
8138
+ if (resourceState.startupClientUnsafe) await retireUnsafeCodexTurnClientBestEffort(turnClient, "startup interrupt");
7471
8139
  } finally {
7472
- releaseCurrentRoute();
8140
+ await releaseCurrentRoute();
7473
8141
  }
7474
8142
  else await activeTurnRoute.cancelTurn();
8143
+ if (params.providerReviewAcknowledgment) throw new Error("Could not continue this chat. Review its latest status before trying again.");
7475
8144
  throw error;
8145
+ } finally {
8146
+ continuation?.dispose();
7476
8147
  }
7477
8148
  };
7478
8149
  if (resourceState.thread.lifecycle.action === "resumed" && (resourceState.thread.lifecycle.activeTurnIds?.length ?? 0) > 0) {
@@ -7496,7 +8167,7 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
7496
8167
  systemPrompt: buildRenderedCodexDeveloperInstructions(),
7497
8168
  prompt: turnState.codexTurnPromptText,
7498
8169
  historyMessages: prompt.codexModelInputHistoryMessages,
7499
- imagesCount: prompt.contextImages.length + (params.images?.length ?? 0),
8170
+ imagesCount: prompt.contextImageGroups.reduce((count, group) => count + group.images.length, 0) + (params.images?.length ?? 0),
7500
8171
  tools
7501
8172
  });
7502
8173
  return {
@@ -7508,7 +8179,7 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
7508
8179
  //#endregion
7509
8180
  //#region extensions/codex/src/app-server/run-attempt-turn-start.ts
7510
8181
  async function startCodexAttemptTurn(resources, turnRuntime, notifications, requestRuntime) {
7511
- const { prompt, state: resourceState, trajectoryRecorder, markTrajectoryEndRecorded, activateNativePreToolUseFailureFallback, releaseCurrentRoute, releaseSandboxExecEnvironment, releaseSharedClientLeaseAndRetireOneShotClient } = resources;
8182
+ const { prompt, state: resourceState, trajectoryRecorder, markTrajectoryEndRecorded } = resources;
7512
8183
  const { context, turnState, systemPromptReport } = prompt;
7513
8184
  const { runtime, historyState, hookContext, hookContextWindowFields, hookRunner } = context;
7514
8185
  const { connection, runtimeParams, effectiveRuntimeProviderId, effectiveRuntimeModelId } = runtime;
@@ -7516,7 +8187,8 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
7516
8187
  const { state, turnIdRef } = turnRuntime;
7517
8188
  const { waitForActiveNativeTurnCompletion } = notifications;
7518
8189
  const { codexModelCallDiagnostics, startCodexTurn, buildLlmInputEvent } = requestRuntime;
7519
- let turn;
8190
+ let started;
8191
+ resourceState.turnStartAttempted = true;
7520
8192
  try {
7521
8193
  codexModelCallDiagnostics.emitStarted();
7522
8194
  runAgentHarnessLlmInputHook({
@@ -7524,10 +8196,10 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
7524
8196
  ctx: hookContext,
7525
8197
  hookRunner
7526
8198
  });
7527
- turn = await startCodexTurn();
8199
+ started = await startCodexTurn();
7528
8200
  } catch (error) {
7529
8201
  let turnStartError = error;
7530
- if (isCodexActiveCompactTurnError(turnStartError)) {
8202
+ if (!params.providerReviewAcknowledgment && isCodexActiveCompactTurnError(turnStartError)) {
7531
8203
  embeddedAgentLog.info("codex app-server turn/start blocked by active compact turn; waiting to retry", { threadId: resourceState.thread.threadId });
7532
8204
  if (await waitForActiveNativeTurnCompletion() && !runAbortController.signal.aborted) {
7533
8205
  emitCodexAppServerEvent(params, {
@@ -7538,13 +8210,13 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
7538
8210
  }
7539
8211
  });
7540
8212
  try {
7541
- turn = await startCodexTurn();
8213
+ started = await startCodexTurn();
7542
8214
  } catch (retryError) {
7543
8215
  turnStartError = retryError;
7544
8216
  }
7545
8217
  }
7546
8218
  }
7547
- if (turn === void 0 && resourceState.thread.connectionScope !== "supervision" && shouldUseFreshCodexThreadAfterContextEngineOverflow({
8219
+ if (started === void 0 && !params.providerReviewAcknowledgment && resourceState.thread.connectionScope !== "supervision" && shouldUseFreshCodexThreadAfterContextEngineOverflow({
7548
8220
  error: turnStartError,
7549
8221
  contextEngineActive: Boolean(activeContextEngine),
7550
8222
  thread: resourceState.thread
@@ -7586,7 +8258,7 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
7586
8258
  }
7587
8259
  });
7588
8260
  try {
7589
- turn = await startCodexTurn();
8261
+ started = await startCodexTurn();
7590
8262
  } catch (retryError) {
7591
8263
  turnStartError = retryError;
7592
8264
  }
@@ -7594,7 +8266,7 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
7594
8266
  } catch (retrySetupError) {
7595
8267
  turnStartError = retrySetupError;
7596
8268
  }
7597
- if (turn === void 0) {
8269
+ if (started === void 0) {
7598
8270
  const usageLimitError = await formatCodexTurnStartUsageLimitError({
7599
8271
  client: resourceState.client,
7600
8272
  error: turnStartError,
@@ -7604,7 +8276,7 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
7604
8276
  signal: runAbortController.signal
7605
8277
  });
7606
8278
  const message = usageLimitError?.message ?? formatErrorMessage(turnStartError);
7607
- if (isInvalidCodexImagePayloadError(message)) await clearCodexBindingAfterInvalidImagePayload(bindingStore, bindingIdentity, {
8279
+ if (!params.providerReviewAcknowledgment && isInvalidCodexImagePayloadError(message)) await clearCodexBindingAfterInvalidImagePayload(bindingStore, bindingIdentity, {
7608
8280
  phase: "turn_start",
7609
8281
  threadId: resourceState.thread.threadId,
7610
8282
  error: message
@@ -7661,33 +8333,6 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
7661
8333
  ctx: hookContext,
7662
8334
  hookRunner
7663
8335
  });
7664
- if ((isIncognitoSessionKey(params.sessionKey) ? await bindingStore.mutate(bindingIdentity, {
7665
- kind: "clear",
7666
- threadId: resourceState.thread.threadId
7667
- }) : true) && !resourceState.startupClientUnsafe) {
7668
- if (!await unsubscribeCodexThreadBestEffort(resourceState.client, {
7669
- threadId: resourceState.thread.threadId,
7670
- timeoutMs: 5e3
7671
- })) await runAgentCleanupStep({
7672
- runId: params.runId,
7673
- sessionId: params.sessionId,
7674
- step: "codex-retire-unsafe-startup-client",
7675
- log: embeddedAgentLog,
7676
- cleanup: async () => closeCodexStartupClientBestEffort(resourceState.client)
7677
- });
7678
- }
7679
- releaseCurrentRoute();
7680
- activateNativePreToolUseFailureFallback();
7681
- resourceState.nativeHookRelay?.unregister();
7682
- await releaseSandboxExecEnvironment();
7683
- await runAgentCleanupStep({
7684
- runId: params.runId,
7685
- sessionId: params.sessionId,
7686
- step: "codex-trajectory-flush-startup-failure",
7687
- log: embeddedAgentLog,
7688
- cleanup: async () => trajectoryRecorder?.flush()
7689
- });
7690
- await releaseSharedClientLeaseAndRetireOneShotClient();
7691
8336
  if (usageLimitError) {
7692
8337
  await markCodexAuthProfileBlockedFromRateLimits({
7693
8338
  params,
@@ -7719,11 +8364,7 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
7719
8364
  throw turnStartError;
7720
8365
  }
7721
8366
  }
7722
- if (!turn) {
7723
- activateNativePreToolUseFailureFallback();
7724
- await releaseSharedClientLeaseAndRetireOneShotClient();
7725
- throw new Error("codex app-server turn/start failed without an error");
7726
- }
8367
+ if (!started) throw new Error("codex app-server turn/start failed without an error");
7727
8368
  const authoritySourceRef = context.attemptTools.scheduledAppAuthoritySourceRef;
7728
8369
  if (resourceState.thread.pluginAppPolicyContext) authoritySourceRef.current = {
7729
8370
  client: resourceState.client,
@@ -7731,9 +8372,9 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
7731
8372
  policyContext: resourceState.thread.pluginAppPolicyContext,
7732
8373
  configCwd: connection.effectiveCwd
7733
8374
  };
7734
- turnIdRef.current = turn.turn.id;
7735
- resourceState.nativeSubagentMonitor?.bindTurn(turn.turn.id);
7736
- return { turn };
8375
+ turnIdRef.current = started.turn.turn.id;
8376
+ resourceState.nativeSubagentMonitor?.bindTurn(started.turn.turn.id);
8377
+ return started;
7737
8378
  }
7738
8379
  //#endregion
7739
8380
  //#region extensions/codex/src/app-server/attempt-deadlines.ts
@@ -7791,36 +8432,30 @@ function createCodexAttemptDeadlineController(params) {
7791
8432
  //#endregion
7792
8433
  //#region extensions/codex/src/app-server/run-attempt-turn-state.ts
7793
8434
  const CODEX_NATIVE_HOOK_RELAY_RENEW_INTERVAL_MS = 6e4;
8435
+ var CodexAttemptState = class {
8436
+ constructor() {
8437
+ this.completed = false;
8438
+ this.abortCleanup = Promise.resolve();
8439
+ this.localCompletionRequested = false;
8440
+ this.terminalTurnNotificationQueued = false;
8441
+ this.sawCodexInterruptMarker = false;
8442
+ this.clientClosedAbort = false;
8443
+ this.shouldDelayNativeHookRelayUnregister = false;
8444
+ this.lifecycleStarted = false;
8445
+ this.lifecycleTerminalEmitted = false;
8446
+ this.nativeHookRelayLastRenewedAt = 0;
8447
+ this.activeAppServerTurnRequests = 0;
8448
+ this.activeLocalProjections = 0;
8449
+ this.projectionClosed = false;
8450
+ this.terminalDynamicToolReleaseCheckScheduled = false;
8451
+ this.currentTurnHadNonTerminalDynamicToolResult = false;
8452
+ }
8453
+ };
7794
8454
  function createCodexAttemptTurnState(resources) {
7795
8455
  const { prompt, state: resourceState, projectorRef, trajectoryRecorder, startupTimeoutMs } = resources;
7796
8456
  const { connection } = prompt.context.runtime;
7797
8457
  const { params, options, runAbortController } = connection;
7798
- const state = {
7799
- latestStartupErrorNotification: void 0,
7800
- rateLimitsRevisionBeforeLastTurnStart: void 0,
7801
- completed: false,
7802
- abortCleanup: Promise.resolve(),
7803
- permissionChangeRestart: void 0,
7804
- localCompletionRequested: false,
7805
- terminalTurnNotificationQueued: false,
7806
- sawCodexInterruptMarker: false,
7807
- timeout: void 0,
7808
- settlementWarning: void 0,
7809
- pendingSettlementStage: void 0,
7810
- clientClosedPromptError: void 0,
7811
- clientClosedDiagnostic: void 0,
7812
- clientClosedAbort: false,
7813
- shouldDelayNativeHookRelayUnregister: false,
7814
- lifecycleStarted: false,
7815
- lifecycleTerminalEmitted: false,
7816
- nativeHookRelayLastRenewedAt: 0,
7817
- activeAppServerTurnRequests: 0,
7818
- activeLocalProjections: 0,
7819
- projectionClosed: false,
7820
- pendingTerminalDynamicToolRelease: void 0,
7821
- terminalDynamicToolReleaseCheckScheduled: false,
7822
- currentTurnHadNonTerminalDynamicToolResult: false
7823
- };
8458
+ const state = new CodexAttemptState();
7824
8459
  const { promise: completion, resolve: resolveCompletion } = createDeferred();
7825
8460
  const settlementExpired = createDeferred();
7826
8461
  const pendingOpenClawDynamicToolCompletionIds = /* @__PURE__ */ new Set();
@@ -7846,19 +8481,28 @@ function createCodexAttemptTurnState(resources) {
7846
8481
  if (!completed) await closeCodexStartupClientBestEffort(resourceState.client);
7847
8482
  return completed;
7848
8483
  };
8484
+ let pendingNativeHookRenewal;
7849
8485
  const renewNativeHookRelayForTurnProgress = () => {
7850
- if (!resourceState.nativeHookRelay || options.nativeHookRelay?.ttlMs !== void 0) return;
8486
+ if (!resourceState.nativeHookRelay || options.nativeHookRelay?.ttlMs !== void 0 || pendingNativeHookRenewal) return;
7851
8487
  const now = Date.now();
7852
8488
  const renewsRecently = now - state.nativeHookRelayLastRenewedAt < CODEX_NATIVE_HOOK_RELAY_RENEW_INTERVAL_MS;
7853
8489
  const expiresSoon = now >= resourceState.nativeHookRelay.expiresAtMs - CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS;
7854
8490
  if (renewsRecently && !expiresSoon) return;
7855
- state.nativeHookRelayLastRenewedAt = now;
7856
- resourceState.nativeHookRelay.renew(resolveCodexNativeHookRelayTtlMs({
8491
+ const relay = resourceState.nativeHookRelay;
8492
+ relay.renew(resolveCodexNativeHookRelayTtlMs({
7857
8493
  explicitTtlMs: void 0,
7858
8494
  attemptTimeoutMs: params.timeoutMs,
7859
8495
  startupTimeoutMs,
7860
8496
  turnStartTimeoutMs: params.timeoutMs
7861
8497
  }));
8498
+ pendingNativeHookRenewal = relay.drain();
8499
+ pendingNativeHookRenewal.then(() => {
8500
+ pendingNativeHookRenewal = void 0;
8501
+ if (resourceState.nativeHookRelay === relay) state.nativeHookRelayLastRenewedAt = now;
8502
+ }, (error) => {
8503
+ pendingNativeHookRenewal = void 0;
8504
+ embeddedAgentLog.debug("native hook relay renewal did not drain", { error });
8505
+ });
7862
8506
  };
7863
8507
  const noteProgress = (reason) => {
7864
8508
  if (state.completed || state.projectionClosed || runAbortController.signal.aborted) return;
@@ -7948,32 +8592,50 @@ async function runCodexAppServerAttempt(params, options) {
7948
8592
  const attemptContext = await preparation.measure("context", () => prepareCodexAttemptContext(runtime, attemptTools));
7949
8593
  const resources = prepareCodexAttemptResources(await preparation.measure("prompt", () => prepareCodexAttemptPrompt(attemptContext)));
7950
8594
  attemptTools.runtimeYieldCompletionClaim.current = () => resources.state.nativeHookRelay?.hasClaimedDirectChild() ?? false;
7951
- await preparation.measure("runtime-start", () => startCodexAttemptRuntime(resources));
7952
- const turnRuntime = createCodexAttemptTurnState(resources);
8595
+ let activeTurnOwnsCleanup = false;
7953
8596
  try {
7954
- const lifecycle = createCodexAttemptLifecycleController(resources, turnRuntime);
7955
- const notifications = createCodexAttemptNotificationController(resources, turnRuntime, lifecycle);
7956
- const serverRequests = createCodexAttemptServerRequestController(resources, turnRuntime, lifecycle);
7957
- const { ensureCurrentThreadRoute } = await preparation.measure("thread-route", () => prepareCodexAttemptRoute(resources, turnRuntime, notifications, serverRequests.handleServerRequest));
7958
- const turnRequest = await preparation.measure("turn-request", () => prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurrentThreadRoute, notifications.waitForActiveNativeTurnCompletion));
7959
- preparation.ready();
7960
- const turnStart = await startCodexAttemptTurn(resources, turnRuntime, notifications, turnRequest);
7961
- if ("result" in turnStart) return turnStart.result;
7962
- const activeTurn = activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notifications, turnStart.turn);
7963
- let finalizedResult;
8597
+ await preparation.measure("runtime-start", () => startCodexAttemptRuntime(resources));
8598
+ const turnRuntime = createCodexAttemptTurnState(resources);
7964
8599
  try {
7965
- await activeTurn.ready;
7966
- finalizedResult = await finalizeCodexAttempt(resources, turnRuntime, lifecycle, notifications, turnRequest, activeTurn);
8600
+ const lifecycle = createCodexAttemptLifecycleController(resources, turnRuntime);
8601
+ const notifications = createCodexAttemptNotificationController(resources, turnRuntime, lifecycle);
8602
+ const serverRequests = createCodexAttemptServerRequestController(resources, turnRuntime, lifecycle, notifications.waitForNativeTerminalItems);
8603
+ const { ensureCurrentThreadRoute } = await preparation.measure("thread-route", () => prepareCodexAttemptRoute(resources, turnRuntime, notifications, serverRequests.handleServerRequest));
8604
+ const turnRequest = await preparation.measure("turn-request", () => prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurrentThreadRoute, notifications.waitForActiveNativeTurnCompletion));
8605
+ preparation.ready();
8606
+ const turnStart = await startCodexAttemptTurn(resources, turnRuntime, notifications, turnRequest);
8607
+ if ("result" in turnStart) return turnStart.result;
8608
+ const activeTurn = activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notifications, turnStart);
8609
+ activeTurnOwnsCleanup = true;
8610
+ let finalizedResult;
8611
+ try {
8612
+ try {
8613
+ await activeTurn.ready;
8614
+ finalizedResult = await finalizeCodexAttempt(resources, turnRuntime, lifecycle, notifications, turnRequest, activeTurn);
8615
+ } finally {
8616
+ await cleanupCodexAttempt(resources, turnRuntime, lifecycle, turnRequest, activeTurn);
8617
+ }
8618
+ } catch (error) {
8619
+ if (!finalizedResult || !turnRuntime.state.pluginRuntimeRefreshStop) throw error;
8620
+ const original = attemptTerminal.project(finalizedResult.terminal).promptError;
8621
+ finalizedResult.terminal = attemptTerminal.merge(finalizedResult.terminal, {
8622
+ kind: "failed",
8623
+ source: "prompt",
8624
+ error: new AggregateError(original ? [original, error] : [error], "Plugin runtime changed, but native continuation failed. Inspect the existing thread before continuing; do not repeat completed actions.")
8625
+ });
8626
+ delete finalizedResult.pluginRuntimeRefreshMessages;
8627
+ delete finalizedResult.settledTurnFinalizationContext;
8628
+ }
8629
+ if (resources.state.executionDisconnectError && !connection.terminalState.explicitCancellationObserved) throw resources.state.executionDisconnectError;
8630
+ return finalizedResult;
7967
8631
  } finally {
7968
- await cleanupCodexAttempt(resources, turnRuntime, lifecycle, turnRequest, activeTurn);
8632
+ turnRuntime.deadlines.dispose();
7969
8633
  }
7970
- if (resources.state.executionDisconnectError && !connection.terminalState.explicitCancellationObserved) throw resources.state.executionDisconnectError;
7971
- return finalizedResult;
7972
8634
  } finally {
7973
- turnRuntime.deadlines.dispose();
8635
+ if (!activeTurnOwnsCleanup) await resources.cleanupBeforeActiveTurn();
7974
8636
  }
7975
8637
  } finally {
7976
- await attemptTools.disposeMcpTools();
8638
+ await attemptTools.disposeTools(connection.runAbortController.signal.aborted ? "cancel" : "error");
7977
8639
  }
7978
8640
  } finally {
7979
8641
  params.abortSignal?.removeEventListener("abort", connection.abortFromUpstream);