@openclaw/codex 2026.9.1 → 2026.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/README.md +2 -0
  2. package/dist/api.js +18 -23
  3. package/dist/apply-B6el6hdO.js +357 -0
  4. package/dist/{attempt-client-cleanup-DYHcRQAa.js → attempt-client-cleanup-D6cR54TV.js} +113 -49
  5. package/dist/{attempt-context-DmQUNWwT.js → attempt-context-AO4DSjE0.js} +11 -9
  6. package/dist/{event-projector-usage-C5mcrGjp.js → attempt-notifications--H8zXR5-.js} +3 -89
  7. package/dist/auth-cache-key-Kq6oerr6.js +498 -0
  8. package/dist/auth-profile-CwzO4_RG.js +43 -0
  9. package/dist/auth-profile-selection-DFu9e167.js +45 -0
  10. package/dist/{binding-connection-E6uDsPlx.js → binding-connection-QqgcPCgk.js} +18 -3
  11. package/dist/{bounded-turn-wzJrwKKe.js → bounded-turn-lRpmZHFv.js} +54 -150
  12. package/dist/build-state-6RL6qohz.js +19 -0
  13. package/dist/{capabilities-dIjNVCYH.js → capabilities-BeqLPbtM.js} +2 -1
  14. package/dist/cli-metadata.js +1 -1
  15. package/dist/client-BEKu4yDx.js +3983 -0
  16. package/dist/command-dispatch-CisT9Jqq.js +21 -0
  17. package/dist/{command-formatters-CLHhEB1Q.js → command-formatters-DeTfrapr.js} +2 -2
  18. package/dist/{command-handlers-CTGb2csu.js → command-handlers-BfIsJEuA.js} +158 -76
  19. package/dist/command-rpc-BMqRQOFX.js +226 -0
  20. package/dist/{compact-_t0HQrUS.js → compact-CGJZ2rlb.js} +89 -49
  21. package/dist/config-JDfTI4VI.js +106 -0
  22. package/dist/{config-BhmfC1s6.js → config-options-jnIFvwxD.js} +208 -654
  23. package/dist/config-runtime-BWf6JLWG.js +11 -0
  24. package/dist/config-utils-C57JiJrU.js +69 -0
  25. package/dist/conversation-binding-CxOlHiix.js +889 -0
  26. package/dist/{plugin-marketplace-discovery-CfFlKqDl.js → conversation-control-CUCrSIQI.js} +36 -155
  27. package/dist/doctor-contract-api.js +8 -9
  28. package/dist/{dynamic-tools-BU9tmlYm.js → dynamic-tools-D-JBoaLt.js} +734 -698
  29. package/dist/{effective-mcp-catalog-DzYBW4h1.js → effective-mcp-catalog-J_5Ep5Um.js} +4 -3
  30. package/dist/{elicitation-input-DMkeki8d.js → elicitation-input-DPoK8F7w.js} +8 -22
  31. package/dist/ephemeral-turn-DSwzP8YY.js +101 -0
  32. package/dist/{event-projector-assistant-message-DHRFXKbS.js → event-projector-assistant-message-BmBM4qBV.js} +23 -27
  33. package/dist/event-projector-usage-CIyrYYid.js +102 -0
  34. package/dist/{harness-DXVZ5II5.js → harness-FR6eQgyO.js} +47 -17
  35. package/dist/harness.js +1 -1
  36. package/dist/history-rejection-B4nOXNXF.js +13 -0
  37. package/dist/{image-payload-sanitizer-CnfoUPCE.js → image-payload-sanitizer-tupAr-2o.js} +29 -15
  38. package/dist/incognito-session-uhrBF6wJ.js +15 -0
  39. package/dist/index.js +413 -2893
  40. package/dist/{isolated-completion-kg9kv4iH.js → isolated-completion-DHBefrzg.js} +12 -21
  41. package/dist/{managed-binary-4-Vjz5GH.js → managed-binary-DrjjRRUD.js} +17 -84
  42. package/dist/managed-thread-store-D1t5jh3G.js +66 -0
  43. package/dist/media-understanding-provider.js +4 -1
  44. package/dist/{model-catalog-CUM68Hkl.js → model-catalog-B_6M04Wo.js} +6 -5
  45. package/dist/{models-sYnEc0gd.js → models-P9k2qAof.js} +4 -4
  46. package/dist/native-execution-policy-umd0tgh4.js +128 -0
  47. package/dist/{native-subagent-monitor-CnH147jH.js → native-subagent-monitor-BbCUKRwC.js} +76 -48
  48. package/dist/node-cli-sessions-rj0ysJwM.js +527 -0
  49. package/dist/{node-exec-server.runtime-CstNHCmC.js → node-exec-server.runtime-BY_ctYnT.js} +54 -23
  50. package/dist/plan-BMJdMOEF.js +711 -0
  51. package/dist/plugin-activation-BQK-f5sV.js +133 -0
  52. package/dist/{thread-resume-C7pyVKeW.js → plugin-app-cache-key-CdSQzWyX.js} +5 -53
  53. package/dist/{plugin-approval-roundtrip-CNe-Xrjx.js → plugin-approval-roundtrip-DeW95wB5.js} +1 -1
  54. package/dist/plugin-inventory-CoRCzWW4.js +325 -0
  55. package/dist/plugin-marketplace-discovery-3akBizzF.js +134 -0
  56. package/dist/{plugin-metadata-cache-BFrPhb2c.js → plugin-metadata-cache-DgNfRAGA.js} +13 -9
  57. package/dist/protocol-CAAcVIKF.js +275 -0
  58. package/dist/{provider-capabilities-DW5p4k9w.js → provider-capabilities-W4tfK5Y5.js} +2 -2
  59. package/dist/{rate-limits-CawQJfUS.js → rate-limits-Bqnx1N4z.js} +1 -1
  60. package/dist/request-CGvGOpeR.js +198 -0
  61. package/dist/{run-attempt-D1hJO4fX.js → run-attempt-DwKds3DN.js} +1498 -1028
  62. package/dist/{runtime-artifact-C3jVimFn.js → runtime-artifact-T-NwqeSH.js} +28 -28
  63. package/dist/sandbox-guard-BcT66fWy.js +165 -0
  64. package/dist/{session-binding-BHuTCRZQ.js → session-binding-CKSepwIC.js} +118 -324
  65. package/dist/session-binding-record-CaZjYwNS.js +256 -0
  66. package/dist/session-catalog-HaDcp7Sv.js +1533 -0
  67. package/dist/session-catalog-adoption-BKLtSrwi.js +251 -0
  68. package/dist/session-catalog-archive-D0i2pGj2.js +56 -0
  69. package/dist/session-catalog-home-id-D7-aLiNt.js +190 -0
  70. package/dist/session-catalog-node-adoption-BUuDYY8R.js +182 -0
  71. package/dist/session-catalog-node-continue-CBtc4wkI.js +238 -0
  72. package/dist/session-catalog-parsing-CdD3JhdM.js +280 -0
  73. package/dist/session-catalog-provenance-DkJHo735.js +124 -0
  74. package/dist/{session-cli-CmFEXMAM.js → session-cli-CbgaHh0f.js} +3 -2
  75. package/dist/session-history-Cz-tOOYj.js +96 -0
  76. package/dist/{session-upstream-marker-C351-5Qw.js → session-history-import-DFN7sWRU.js} +5 -49
  77. package/dist/session-history-read-cxPUxyVV.js +50 -0
  78. package/dist/session-history-worker-runtime.js +60 -0
  79. package/dist/{settled-turn-context-D3SNWtUn.js → session-history.worker-CNEvGaU5.js} +75 -69
  80. package/dist/session-history.worker.js +2 -0
  81. package/dist/{session-initialization-BeWzjGiR.js → session-initialization-Jo86C60I.js} +2 -1
  82. package/dist/{session-permission-policy-DeWVTCMj.js → session-permission-policy-B22InTp_.js} +4 -7
  83. package/dist/{session-retirement-BujDjXVF.js → session-retirement-BSNG_mhf.js} +8 -7
  84. package/dist/session-upstream-marker-D15C9NHp.js +46 -0
  85. package/dist/settled-turn-context-Du6gdUDd.js +45 -0
  86. package/dist/{settled-turn-finalizer-BVJx4yoc.js → settled-turn-finalizer-BuLUrZl2.js} +54 -42
  87. package/dist/shared-client-CVhez2Uo.js +2911 -0
  88. package/dist/{side-question-BTZAII4l.js → side-question-DLbr2vUU.js} +135 -191
  89. package/dist/source-Da-h6f7e.js +518 -0
  90. package/dist/thread-archive-guard-D1bP04Tf.js +53 -0
  91. package/dist/{thread-lifecycle-DAcEJ_Om.js → thread-lifecycle-D1pzRwHB.js} +467 -977
  92. package/dist/{thread-ownership-zGokd8i1.js → thread-ownership-D6oASVRu.js} +15 -17
  93. package/dist/{thread-policy-g4KbJPzp.js → thread-policy-D8kI7yAO.js} +2 -2
  94. package/dist/thread-resume-T5cveyxA.js +52 -0
  95. package/dist/timeout-BVw8aaM1.js +33 -0
  96. package/dist/transcript-mirror-attestation-DibE0krH.js +89 -0
  97. package/dist/{transcript-mirror-DTUxFWa_.js → transcript-mirror-t5TOccct.js} +111 -379
  98. package/dist/{transport-stdio-CyPldczm.js → transport-stdio-BGjHoYLr.js} +80 -48
  99. package/dist/{upstream-session-fork-CYlBIGVt.js → upstream-session-fork-De-sLJ2r.js} +94 -166
  100. package/dist/{usage-Bj-rrzkR.js → usage-CPPTmBvZ.js} +4 -3
  101. package/dist/version-omm6IPI3.js +12 -0
  102. package/dist/{web-search-provider.runtime-D2FWSK4w.js → web-search-provider.runtime-CSVgQL0w.js} +3 -3
  103. package/openclaw.plugin.json +22 -1
  104. package/package.json +6 -6
  105. package/dist/request-DYAI5FQE.js +0 -475
  106. package/dist/session-catalog-yyvTWFOw.js +0 -3120
  107. package/dist/session-history-C5J1AKzG.js +0 -87
  108. package/dist/shared-client-DxcfF9KX.js +0 -7494
  109. package/dist/{auth-binding-Ds_QVm-X.js → auth-binding-Co2Vfcf_.js} +1 -1
@@ -1,54 +1,110 @@
1
- import { $t as isJsonObject, A as TURN_FINALIZE_DRAIN_ABORT_GRACE_MS, B as isCodexAppServerIndeterminateRequestCancellationError, Bt as resolveCodexAppServerAuthProfileIdForAgent, Ct as ensureCodexAppServerClientRuntime, Dt as protectCodexAppServerLiveThread, F as withCodexStartupTimeout, Ft as readRecentCodexRateLimits, H as isCodexAppServerOverloadError, Ht as resolveCodexAppServerFallbackApiKeyCacheKey, I as getCodexAppServerClientInstanceId, It as rememberCodexRateLimitsRead, K as resolveCodexAppServerClientInstanceId, L as isCodexAppServerApprovalRequest, M as isCodexAppServerStartupError, Mt as withAbortableTimeout, P as resolveCodexStartupTimeoutMs, Pt as readCodexRateLimitsRevision, Qt as flattenCodexDynamicToolFunctions, R as isCodexAppServerBrokenPipeError, Rt as resolveCodexAppServerAuthAccountCacheKey, Ut as resolveCodexAppServerPreparedApiKeyCacheKey, V as isCodexAppServerIndeterminateTransportError, W as isCodexAppServerRequestTimeoutError, Wt as resolveCodexAppServerPreparedAuthHandoff, _t as resolveCodexToolAbortTerminalReason, a as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, at as readCodexTurnCompletedNotification, c as getLeasedSharedCodexAppServerClient, ct as hasPendingDynamicToolTerminalDiagnostic, dt as resolveDynamicToolCallTimeoutMs, f as readCodexAppServerClientDesktopGenerationFingerprint, ft as resolveTerminalDynamicToolBatchAction, gt as toCodexDynamicToolProtocolResponse, h as releaseLeasedSharedCodexAppServerClient, ht as toCodexDynamicToolProgressResponse, i as clearSharedCodexAppServerClientIfCurrent, it as readCodexTurn, j as TURN_TERMINAL_SETTLEMENT_TIMEOUT_MS, k as CodexAppServerStartupError, l as getSharedCodexAppServerClient, lt as isDynamicToolTerminalDiagnosticEvent, mt as shouldReleaseTurnAfterTerminalDynamicTool, nt as readCodexDynamicToolCallParams, ot as createCodexElicitationResponse, pt as shouldBlockTerminalReleaseForNonTerminalDynamicToolResult, q as CodexAppServerRpcError, s as createIsolatedCodexAppServerClient, st as handleDynamicToolCallWithTimeout, tt as assertCodexTurnStartResponse, u as isCodexAppServerStartSelectionChangedError, ut as isMatchingDynamicToolTerminalDiagnostic, v as retainSharedCodexAppServerClientIfCurrent, y as retireSharedCodexAppServerClientIfCurrent, z as isCodexAppServerConnectionClosedError, zt as resolveCodexAppServerAuthProfileId } from "./shared-client-DxcfF9KX.js";
2
- import { C as isCodexPairedNodeRemoteExecPlacementSandbox, D as resolveCodexPluginsPolicy, E as readCodexPluginConfig, F as resolveCodexAppServerHomeDir, O as resolveOpenClawExecPolicyForCodexAppServer, T as isCodexSandboxExecServerEnabled, d as shouldAutoApproveCodexAppServerApprovals, f as withMcpElicitationsApprovalPolicy, h as resolveCodexModelBackedReviewerPolicyContext, l as hasCodexMcpToolApprovalOverrides, m as canUseCodexModelBackedApprovalsReviewerForModel, o as resolveCodexComputerUseConfig, r as resolveCodexAppServerHomeScope, w as isCodexRemoteExecPlacementSandbox, y as readCodexRequirementsToml } from "./config-BhmfC1s6.js";
3
- import { c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, i as createCodexSessionGenerationSupersededError, l as resolveCodexRunSessionBindingAuthority, u as scopeCodexRunBindingStore } from "./session-binding-BHuTCRZQ.js";
4
- import { B as readNonNegativeInteger, E as itemStatus, H as splitPlanText, I as readItem, L as readItemString, M as extractRawAssistantText, O as matchesCodexSnapshotTurn, P as readCodexErrorNotificationMessage, T as itemName, V as readNullableString, a as closeCodexStartupClientBestEffort, d as unsubscribeCodexThreadBestEffort, f as CODEX_APP_SERVER_NATIVE_TURN_WAIT_TIMEOUT_MS, g as readCodexNotificationThreadId, h as isCodexNotificationForTurn, j as shouldSynthesizeToolProgressForItem, k as shouldClearTerminalPresentationForNativeItem, l as retireUnsafeCodexTurnClientBestEffort, m as CodexProjectionDiagnostics, o as interruptCodexTurnAndWaitBestEffort, p as getCodexAppServerTurnRouter, r as CodexAppServerUnsafeSubscriptionError, u as terminateCodexBackgroundTerminals } from "./attempt-client-cleanup-DYHcRQAa.js";
5
- import { a as codexExecutionToolName, c as isPendingOpenClawDynamicToolCompletionNotification, d as isTerminalTurnStatus, f as readCodexNotificationItem, h as updateActiveTurnItemIds, l as isRawFunctionToolOutputCompletionNotification, m as readRawResponseToolCallId, p as readNotificationItemId, r as projectCodexThreadUsageUpdate, s as isCodexTurnAbortMarkerNotification, t as CodexResponseCompletionProjection } from "./event-projector-usage-C5mcrGjp.js";
6
- import { i as buildCodexPluginAppCacheKey, r as buildCodexAppServerRuntimeFingerprint, s as defaultCodexAppInventoryCache } from "./thread-resume-C7pyVKeW.js";
7
- import { A as readCodexMcpToolConnectorId, B as emitCodexNativePreToolUseFailureDiagnostic, C as buildDeveloperInstructions, Ct as buildCodexPluginThreadConfig, D as resolveCodexAppServerThreadModelSelection, Dt as mergeCodexThreadConfigs, H as resolveCodexNativeHookRelayTtlMs, I as buildCodexNativeHookRelayConfig, L as buildCodexNativeHookRelayDisabledConfig, M as isCodexAppServerProfilerEnabled, Ot as shouldBuildCodexPluginThreadConfig, P as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, Pt as resolveRecoverableCodexPluginConfigKeys, S as resolveCodexWebSearchPlan, Tt as buildCodexPluginThreadConfigTimeoutFallback, U as scheduleCodexNativeHookRelayUnregister, V as resolveCodexNativeHookRelayEvents, X as loadCodexNativeToolCatalog, Y as hasCodexNativeToolCatalog, _t as neutralizeCodexExplicitMentionSigils, at as filterCodexDynamicTools, bt as resolveCodexContextEngineProjectionReserveTokens, c as assertScheduledCodexAppAuthorityRuntime, d as buildScheduledCodexAppServerConnectionIdentity, f as captureScheduledCodexAppAuthority, ft as resolveCodexDynamicToolsLoadingForRuntime, gt as fitCodexProjectedContextForTurnStart, h as resolveScheduledCodexAppCreatorCaptureDecision, ht as buildCodexContinuityCalibration, i as buildCodexUserInput, j as readCodexMcpToolUiVisibility, l as buildLegacyScheduledCodexAppRecoveryPrompt, lt as isSystemAgentOnlyCodexDynamicToolAllowlist, m as readCurrentCodexScheduledAppPolicy$1, n as buildTurnCollaborationMode, nt as codexLegacyDynamicToolsFingerprint, p as intersectCodexPluginThreadConfigWithScheduledAuthority, pt as buildContextEngineBinding, r as buildTurnStartParams, s as startOrResumeThread, tt as codexDynamicToolsFingerprint, u as buildScheduledCodexAppAuthorityInputFingerprint, vt as projectContextEngineAssemblyForCodex, wt as buildCodexPluginThreadConfigInputFingerprint, xt as resolveCodexContinuityProjectionMaxChars, yt as resolveCodexContextEngineProjectionMaxChars, z as createCodexNativeHookRelay } from "./thread-lifecycle-DAcEJ_Om.js";
8
- import { l as isIncognitoSessionKey, r as retainCodexAppServerBindingSubscription } from "./thread-ownership-zGokd8i1.js";
9
- import { a as buildCodexUserPromptMessage, i as mirrorPromptAtTurnStartBestEffort, l as hasCodexMirrorOrigin, n as createCodexAppServerUserMessagePersistenceNotifier, o as promptSnapshot, s as applyCodexTranscriptTaint, t as codexTranscriptMirrorRuntime } from "./transcript-mirror-DTUxFWa_.js";
10
- import { n as CodexThreadPolicyHandoffError } from "./thread-policy-g4KbJPzp.js";
1
+ import { l as sessionBindingIdentity, n as assertCodexBindingMayBeReplaced } from "./session-binding-record-CaZjYwNS.js";
2
+ import { n as defineCodexBuildState } from "./build-state-6RL6qohz.js";
3
+ 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-jnIFvwxD.js";
4
+ 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-CAAcVIKF.js";
5
+ 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-BEKu4yDx.js";
6
+ import { c as resolveCodexAppServerPreparedApiKeyCacheKey, m as retireSharedCodexAppServerClientIfCurrent, s as resolveCodexAppServerFallbackApiKeyCacheKey } from "./auth-cache-key-Kq6oerr6.js";
7
+ import { t as isIncognitoSessionKey } from "./incognito-session-uhrBF6wJ.js";
8
+ import { a as shouldRefreshCodexRateLimitsForUsageLimitMessage, i as resolveCodexUsageLimitResetAtMs, n as formatCodexUsageLimitErrorMessage } from "./rate-limits-Bqnx1N4z.js";
9
+ import { a as formatCodexDisplayText } from "./command-formatters-DeTfrapr.js";
10
+ import { r as CodexAppServerRpcError } from "./session-catalog-provenance-DkJHo735.js";
11
+ import { t as CODEX_CONTROL_METHODS } from "./capabilities-BeqLPbtM.js";
11
12
  import { r as readMirrorIdentity, t as attachCodexMirrorIdentity } from "./upstream-prompt-provenance-LphB8slG.js";
12
- import { t as CODEX_CONTROL_METHODS } from "./capabilities-dIjNVCYH.js";
13
- import { a as shouldRefreshCodexRateLimitsForUsageLimitMessage, i as resolveCodexUsageLimitResetAtMs, n as formatCodexUsageLimitErrorMessage } from "./rate-limits-CawQJfUS.js";
14
- import { a as formatCodexDisplayText } from "./command-formatters-CLHhEB1Q.js";
15
- import { a as resolveCodexAppServerForModelProvider, i as resolveCodexSessionPermissionCwd, n as applyCodexSessionPermissionPolicy, r as resolveCodexEffectiveSessionPermissionPolicy } from "./session-permission-policy-DeWVTCMj.js";
16
- import { n as resolveCodexBindingAppServerConnection } from "./binding-connection-E6uDsPlx.js";
17
- import { n as prepareCodexAppServerAuthBinding } from "./auth-binding-Ds_QVm-X.js";
13
+ import { n as buildCodexAppServerRuntimeFingerprint, r as buildCodexPluginAppCacheKey, s as defaultCodexAppInventoryCache } from "./plugin-app-cache-key-CdSQzWyX.js";
14
+ import { t as withAbortableTimeout } from "./timeout-BVw8aaM1.js";
15
+ import { n as resolveOpenClawExecPolicyForCodexAppServer, t as canUseCodexModelBackedApprovalsReviewerForModel } from "./config-JDfTI4VI.js";
16
+ import { o as hasCodexMirrorOrigin, t as applyCodexTranscriptTaint } from "./transcript-mirror-attestation-DibE0krH.js";
17
+ import { n as prepareCodexAppServerAuthBinding } from "./auth-binding-Co2Vfcf_.js";
18
+ import { i as resolveCodexBindingAppServerConnection, t as assertCodexSessionRuntimeOwnership } from "./binding-connection-QqgcPCgk.js";
19
+ import { i as resolveCodexAppServerAuthProfileIdForAgent, r as resolveCodexAppServerAuthProfileId } from "./auth-profile-CwzO4_RG.js";
20
+ import { B as prepareCodexWorkspaceReferences, D as TURN_FINALIZE_DRAIN_ABORT_GRACE_MS, E as CodexAppServerStartupError, F as ensureCodexAppServerClientRuntime, J as rememberCodexRateLimitsRead, K as readCodexRateLimitsRevision, M as withCodexStartupTimeout, O as TURN_TERMINAL_SETTLEMENT_TIMEOUT_MS, V as protectCodexAppServerLiveThread, X as resolveCodexAppServerAuthAccountCacheKey, Z as resolveCodexAppServerPreparedAuthHandoff, a as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, c as getLeasedSharedCodexAppServerClient, f as readCodexAppServerClientDesktopGenerationFingerprint, h as releaseLeasedSharedCodexAppServerClient, i as clearSharedCodexAppServerClientIfCurrent, j as resolveCodexStartupTimeoutMs, k as isCodexAppServerStartupError, l as getSharedCodexAppServerClient, q as readRecentCodexRateLimits, s as createIsolatedCodexAppServerClient, u as isCodexAppServerStartSelectionChangedError, v as retainSharedCodexAppServerClientIfCurrent } from "./shared-client-CVhez2Uo.js";
21
+ import { F as readCodexProviderRefusal, H as readNullableString, L as readItem, M as extractRawAssistantText, O as matchesCodexSnapshotTurn, P as readCodexErrorNotificationMessage, R as readItemString, U as splitPlanText, V as readNonNegativeInteger, a as closeCodexStartupClientBestEffort, d as unsubscribeCodexThreadBestEffort, f as CODEX_APP_SERVER_NATIVE_TURN_WAIT_TIMEOUT_MS, g as readCodexNotificationThreadId, h as isCodexNotificationForTurn, k as shouldClearTerminalPresentationForNativeItem, l as retireUnsafeCodexTurnClientBestEffort, m as CodexProjectionDiagnostics, o as interruptCodexTurnAndWaitBestEffort, p as getCodexAppServerTurnRouter, r as CodexAppServerUnsafeSubscriptionError, u as terminateCodexBackgroundTerminals } from "./attempt-client-cleanup-D6cR54TV.js";
22
+ 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--H8zXR5-.js";
23
+ import { t as CodexUsageProjection } from "./event-projector-usage-CIyrYYid.js";
24
+ import { s as resolveRecoverableCodexPluginConfigKeys } from "./plugin-inventory-CoRCzWW4.js";
25
+ import { $ as codexLegacyDynamicToolsFingerprint, A as readCodexMcpToolConnectorId, B as emitCodexNativePreToolUseFailureDiagnostic, C as buildDeveloperInstructions, Ct as buildCodexPluginThreadConfigTimeoutFallback, D as resolveCodexAppServerThreadModelSelection, Dt as shouldBuildCodexPluginThreadConfig, H as resolveCodexNativeHookRelayTtlMs, I as buildCodexNativeHookRelayConfig, K as hasCodexNativeToolCatalog, L as buildCodexNativeHookRelayDisabledConfig, M as isCodexAppServerProfilerEnabled, P as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, Q as codexDynamicToolsFingerprint, S as resolveCodexWebSearchPlan, St as buildCodexPluginThreadConfigInputFingerprint, Tt as mergeCodexThreadConfigs, U as scheduleCodexNativeHookRelayUnregister, V as resolveCodexNativeHookRelayEvents, _t as projectContextEngineAssemblyForCodex, bt as resolveCodexContinuityProjectionMaxChars, c as assertScheduledCodexAppAuthorityRuntime, d as buildScheduledCodexAppServerConnectionIdentity, f as captureScheduledCodexAppAuthority, ft as CodexContextAttachmentError, gt as neutralizeCodexExplicitMentionSigils, h as resolveScheduledCodexAppCreatorCaptureDecision, ht as isCodexDurableCustomMessage, i as buildCodexUserInput, j as readCodexMcpToolUiVisibility, l as buildLegacyScheduledCodexAppRecoveryPrompt, lt as resolveCodexDynamicToolsLoadingForRuntime, m as readCurrentCodexScheduledAppPolicy$1, mt as fitCodexProjectedContextForTurnStart, n as buildTurnCollaborationMode, nt as filterCodexDynamicTools, ot as isSystemAgentOnlyCodexDynamicToolAllowlist, p as intersectCodexPluginThreadConfigWithScheduledAuthority, pt as buildCodexContinuityCalibration, q as loadCodexNativeToolCatalog, r as buildTurnStartParams, s as startOrResumeThread, u as buildScheduledCodexAppAuthorityInputFingerprint, ut as buildContextEngineBinding, vt as resolveCodexContextEngineProjectionMaxChars, xt as buildCodexPluginThreadConfig, yt as resolveCodexContextEngineProjectionReserveTokens, z as createCodexNativeHookRelay } from "./thread-lifecycle-D1pzRwHB.js";
26
+ import { a as resolveCodexRunSessionBindingAuthority, n as createCodexSessionGenerationSupersededError, o as resolveCodexSessionBinding, s as scopeCodexRunBindingStore } from "./session-binding-CKSepwIC.js";
27
+ import { n as CodexThreadPolicyHandoffError } from "./thread-policy-D8kI7yAO.js";
28
+ import { r as retainCodexAppServerBindingSubscription } from "./thread-ownership-D6oASVRu.js";
29
+ import { i as mirrorPromptAtTurnStartBestEffort, n as createCodexAppServerUserMessagePersistenceNotifier, o as buildCodexUserPromptMessage, s as promptSnapshot, t as codexTranscriptMirrorRuntime } from "./transcript-mirror-t5TOccct.js";
18
30
  import { t as persistCodexContextCompactionActivity } from "./context-compaction-activity-CMla5FPJ.js";
19
- import { i as createAssistantReasoningMessage, n as createAssistantCommentaryMessage, r as createAssistantMessage, t as createAssistantAsyncMessage } from "./event-projector-assistant-message-DHRFXKbS.js";
20
- import { a as CodexToolProgressProjection, c as isCodexCommandBearingToolCall, d as sanitizeCodexToolResponse, i as CodexToolTranscriptProjection, l as resolveCodexToolProgressDetailMode, n as CodexEventProjection, o as shouldEmitTranscriptToolProgress, r as emitCodexAgentEvent, s as inferCodexDynamicToolMeta, t as codexNativeSubagentMonitorRuntime, u as sanitizeCodexToolArguments } from "./native-subagent-monitor-CnH147jH.js";
21
- import { a as requestPluginApprovalOutcome } from "./plugin-approval-roundtrip-CNe-Xrjx.js";
22
- import { S as shouldWarnCodexDynamicToolBuildStageSummary, T as CodexNativeToolLifecycleProjector, _ as resolveCodexMessageToolProvider, a as emitDynamicToolStartedDiagnostic, b as shouldEnableCodexAppServerNativeToolSurface, c as ensureCodexSandboxExecServerEnvironment, d as createCodexDynamicToolBuildStageTracker, f as disableCodexPluginThreadConfig, g as resolveCodexExternalSandboxPolicyForOpenClawSandbox, h as resolveCodexAppServerHookChannelId, i as emitDynamicToolErrorDiagnostic, l as releaseCodexSandboxExecServerEnvironment, m as resolveCodexAppServerExecutionCwd, n as projectCodexExecutableDynamicTools, o as emitDynamicToolTerminalDiagnostic, p as formatCodexDynamicToolBuildStageSummary, r as routeCodexAppServerElicitationRequest, s as handleCodexAppServerApprovalRequest, t as createCodexDynamicToolBridge, u as buildDynamicTools, w as readBoundedCodexRemoteWorkspaceFile, x as shouldRequireCodexSandboxExecServerEnvironment, y as resolveCodexSandboxEnvironmentSelection } from "./dynamic-tools-BU9tmlYm.js";
23
- import { t as resolveCodexProviderWebSearchSupport } from "./provider-capabilities-DW5p4k9w.js";
24
- 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-DmQUNWwT.js";
25
- import { n as captureCodexSettledTurnFinalizationContext } from "./settled-turn-context-D3SNWtUn.js";
26
- import { a as runCodexComputerUseLiveTest, n as ensureCodexComputerUse, t as defaultCodexPluginMetadataCache } from "./plugin-metadata-cache-BFrPhb2c.js";
31
+ import { i as createAssistantReasoningMessage, n as createAssistantCommentaryMessage, r as createAssistantMessage, t as createAssistantAsyncMessage } from "./event-projector-assistant-message-BmBM4qBV.js";
32
+ 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-D-JBoaLt.js";
33
+ import { t as resolveCodexProviderWebSearchSupport } from "./provider-capabilities-W4tfK5Y5.js";
34
+ import { a as CodexToolProgressProjection, c as isCodexCommandBearingToolCall, d as sanitizeCodexToolResponse, i as CodexToolTranscriptProjection, l as resolveCodexToolProgressDetailMode, n as CodexEventProjection, o as shouldEmitTranscriptToolProgress, r as emitCodexAgentEvent, s as inferCodexDynamicToolMeta, t as codexNativeSubagentMonitorRuntime, u as sanitizeCodexToolArguments } from "./native-subagent-monitor-BbCUKRwC.js";
35
+ import { a as requestPluginApprovalOutcome } from "./plugin-approval-roundtrip-DeW95wB5.js";
36
+ import { a as resolveCodexAppServerForModelProvider, i as resolveCodexSessionPermissionCwd, n as applyCodexSessionPermissionPolicy, r as resolveCodexEffectiveSessionPermissionPolicy } from "./session-permission-policy-B22InTp_.js";
37
+ 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-AO4DSjE0.js";
38
+ import { n as captureCodexSettledTurnFinalizationContext } from "./settled-turn-context-Du6gdUDd.js";
39
+ import { a as runCodexComputerUseLiveTest, n as ensureCodexComputerUse, t as defaultCodexPluginMetadataCache } from "./plugin-metadata-cache-DgNfRAGA.js";
27
40
  import { asOptionalRecord, normalizeOptionalString, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
28
- import fs from "node:fs/promises";
41
+ import { createHash } from "node:crypto";
42
+ import { resolveAgentConfig, resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
43
+ import { MAX_TIMER_TIMEOUT_MS, asDateTimestampMs, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
29
44
  import path from "node:path";
45
+ import fs from "node:fs/promises";
46
+ import { addAbortListener } from "node:events";
47
+ import { truncateUtf16Safe, truncateUtf8Prefix } from "openclaw/plugin-sdk/text-utility-runtime";
48
+ import { Buffer as Buffer$1 } from "node:buffer";
30
49
  import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
31
- import { MAX_TIMER_TIMEOUT_MS, asDateTimestampMs, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
32
- import { AgentHarnessPreflightError, CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, FAST_MODE_AUTO_PROGRESS_KIND, agentHarnessAttemptTerminal, agentHarnessStructuredInput, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentEndSideEffects, bootstrapHarnessContextEngine, buildAgentHookContextChannelFields, buildEmbeddedForegroundPromptContext, buildHarnessContextEngineRuntimeContext, callGatewayTool, cancelPendingAgentQuestionForSession, claimPendingAgentQuestionAnswer, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, detectAndLoadAgentHarnessPromptImages, embeddedAgentLog, emitAgentEvent, emptyAgentHarnessUserInputAnswers, formatErrorMessage, formatFastModeAutoProgressText, getAgentHarnessHookRunner, hasBeforeToolCallPolicy, isActiveHarnessContextEngine, isHostScopedAgentToolActive, loadCodexBundleMcpThreadConfig, materializeRequesterScopedMcpToolsForHarnessRun, prepareHarnessNativeMcpAppPreview, projectAgentHarnessTranscriptMessageForDisplay, resolveAgentDir, resolveAgentHarnessBeforePromptBuildResult, resolveAgentRunAbortLifecycleFields, resolveAttemptFsWorkspaceOnly, resolveContextEngineOwnerPluginId, resolveFastModeForElapsed, resolveSandboxContext, resolveUserPath, runAgentCleanupStep, runAgentEndSideEffects, runAgentHarnessAfterCompactionHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
33
50
  import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
34
- import { createHash } from "node:crypto";
51
+ import { isDeepStrictEqual } from "node:util";
35
52
  import { markAuthProfileBlockedUntil, resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
36
- import { resolveAgentConfig as resolveAgentConfig$1, resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
53
+ 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";
37
54
  import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
38
55
  import { parseSqliteSessionFileMarker } from "openclaw/plugin-sdk/session-store-runtime";
39
- import { addAbortListener } from "node:events";
40
- import { truncateUtf16Safe, truncateUtf8Prefix } from "openclaw/plugin-sdk/text-utility-runtime";
41
56
  import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent, emitTrustedDiagnosticEventWithPrivateData, freezeDiagnosticTraceContext, onInternalDiagnosticEvent, resolveDiagnosticModelContentCapturePolicy } from "openclaw/plugin-sdk/diagnostic-runtime";
42
57
  import { captureFinalCodexCronCreatorToolAllowlist, formatMcpCodexApprovalRemedy, materializeStaticMcpToolsForHarnessRun, resolveCodexMcpToolOverridesForAgent, resolveCodexTtsProvenanceTransfer } from "openclaw/plugin-sdk/codex-mcp-projection";
43
- import { hasPromptImageInput } from "openclaw/plugin-sdk/session-transcript-runtime";
44
- import { Buffer as Buffer$1 } from "node:buffer";
45
- import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
58
+ import { appendSessionYieldContext, hasPromptImageInput } from "openclaw/plugin-sdk/session-transcript-runtime";
46
59
  import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-local-roots";
47
60
  import { isSilentReplyPayloadText } from "openclaw/plugin-sdk/reply-chunking";
48
61
  import { generatedImageAssetFromBase64 } from "openclaw/plugin-sdk/image-generation";
49
62
  import { resolveGeneratedMediaMaxBytes } from "openclaw/plugin-sdk/media-generation-runtime";
50
63
  import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
51
64
  import { stripInvisibleUnicode } from "openclaw/plugin-sdk/web-content-extractor";
65
+ import { acknowledgeInternalToolResult } from "openclaw/plugin-sdk/agent-harness-tool-runtime";
66
+ import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
67
+ //#region extensions/codex/src/app-server/attempt-preparation-timing.ts
68
+ /** Records startup before the native turn owns inference and tool execution. */
69
+ function createCodexAttemptPreparationTiming(params) {
70
+ const tracker = createCodexDynamicToolBuildStageTracker();
71
+ const profilerEnabled = isCodexAppServerProfilerEnabled(params.config);
72
+ const totalWarnMs = profilerEnabled ? 1e3 : 1e4;
73
+ const stageWarnMs = profilerEnabled ? 500 : 5e3;
74
+ const log = (stage, outcome) => {
75
+ const summary = tracker.snapshot();
76
+ const lastStage = summary.stages.at(-1);
77
+ const slowTotal = outcome !== "completed" && summary.totalMs >= totalWarnMs;
78
+ const slowStage = outcome !== "ready" && (lastStage?.durationMs ?? 0) >= stageWarnMs;
79
+ if (!slowTotal && !slowStage) return;
80
+ embeddedAgentLog.warn(`codex app-server preparation timings runId=${params.runId} sessionId=${params.sessionId} stage=${stage} outcome=${outcome} totalMs=${summary.totalMs} stages=${formatCodexDynamicToolBuildStageSummary(summary)}`, {
81
+ runId: params.runId,
82
+ sessionId: params.sessionId,
83
+ sessionKey: params.sessionKey,
84
+ stage,
85
+ outcome,
86
+ totalMs: summary.totalMs,
87
+ stages: summary.stages
88
+ });
89
+ };
90
+ return {
91
+ async measure(stage, run) {
92
+ let outcome = "error";
93
+ try {
94
+ const result = await run();
95
+ outcome = "completed";
96
+ return result;
97
+ } finally {
98
+ tracker.mark(stage);
99
+ log(stage, outcome);
100
+ }
101
+ },
102
+ ready() {
103
+ log("native-turn-handoff", "ready");
104
+ }
105
+ };
106
+ }
107
+ //#endregion
52
108
  //#region extensions/codex/src/app-server/attempt-steering.ts
53
109
  /**
54
110
  * Debounced steering queue for forwarding user messages to an active Codex
@@ -130,30 +186,67 @@ function createCodexSteeringQueue(params) {
130
186
  closeQueue(/* @__PURE__ */ new Error("codex app-server steering queue cancelled"));
131
187
  };
132
188
  const sendBatch = async (items) => {
133
- const liveItems = items.filter((item) => !item.settled);
189
+ let liveItems = items.filter((item) => !item.settled);
134
190
  if (liveItems.length === 0) return;
135
191
  let clientUserMessageId;
192
+ let skippedRevokedBatch = false;
136
193
  try {
137
194
  assertActive();
138
- const input = [];
195
+ const prepared = /* @__PURE__ */ new Map();
196
+ const isCurrent = (item) => {
197
+ if (item.settled) return false;
198
+ try {
199
+ item.assertCurrent();
200
+ return true;
201
+ } catch (error) {
202
+ rejectItem(item, error);
203
+ return false;
204
+ }
205
+ };
139
206
  for (const item of liveItems) {
140
- input.push(...await params.prepareMessage(item.text, item));
207
+ if (!isCurrent(item)) continue;
208
+ try {
209
+ prepared.set(item, await params.prepareMessage(item.text, item));
210
+ } catch (error) {
211
+ if (isCurrent(item)) throw error;
212
+ }
213
+ assertActive();
214
+ isCurrent(item);
215
+ }
216
+ liveItems = liveItems.filter(isCurrent);
217
+ if (liveItems.length === 0) return;
218
+ if (params.beforeSubmit) {
219
+ await params.beforeSubmit(liveItems);
141
220
  assertActive();
221
+ liveItems = liveItems.filter(isCurrent);
222
+ if (liveItems.length === 0) return;
142
223
  }
143
224
  clientUserMessageId = `openclaw:${params.turnId}:steer:${++batchSequence}`;
144
225
  dispatchedBatches.set(clientUserMessageId, { items: liveItems });
145
- await params.client.request("turn/steer", {
226
+ const request = {
146
227
  threadId: params.threadId,
147
228
  expectedTurnId: params.turnId,
148
- input,
229
+ input: liveItems.flatMap((item) => prepared.get(item) ?? []),
149
230
  clientUserMessageId
150
- }, {
231
+ };
232
+ await params.client.request("turn/steer", request, {
151
233
  timeoutMs: params.requestTimeoutMs,
152
- signal: params.signal
234
+ signal: params.signal,
235
+ assertCurrent: () => {
236
+ assertActive();
237
+ liveItems = liveItems.filter(isCurrent);
238
+ request.input = liveItems.flatMap((item) => prepared.get(item) ?? []);
239
+ dispatchedBatches.set(request.clientUserMessageId, { items: liveItems });
240
+ if (liveItems.length === 0) {
241
+ skippedRevokedBatch = true;
242
+ throw new Error("Codex steering batch has no authorized inputs");
243
+ }
244
+ }
153
245
  });
154
246
  for (const item of liveItems) reportItemAcceptance(item, true);
155
247
  } catch (error) {
156
248
  if (clientUserMessageId) dispatchedBatches.delete(clientUserMessageId);
249
+ if (skippedRevokedBatch) return;
157
250
  const acceptedUnconfirmed = clientUserMessageId !== void 0 && (isCodexAppServerIndeterminateRequestCancellationError(error) || isCodexAppServerIndeterminateTransportError(error));
158
251
  for (const item of liveItems) {
159
252
  if (acceptedUnconfirmed) reportItemAcceptance(item, true);
@@ -180,7 +273,7 @@ function createCodexSteeringQueue(params) {
180
273
  send.catch(() => void 0);
181
274
  return send;
182
275
  };
183
- const createPendingMessage = (text, options) => {
276
+ const createPendingMessage = (text, options, assertCurrent = () => {}) => {
184
277
  let resolveDelivery;
185
278
  let rejectDelivery;
186
279
  const delivery = new Promise((resolve, reject) => {
@@ -189,6 +282,7 @@ function createCodexSteeringQueue(params) {
189
282
  });
190
283
  const item = {
191
284
  ...options,
285
+ assertCurrent,
192
286
  acceptance: "open",
193
287
  text,
194
288
  resolve: resolveDelivery,
@@ -204,14 +298,15 @@ function createCodexSteeringQueue(params) {
204
298
  params.signal.addEventListener("abort", abortQueue, { once: true });
205
299
  if (params.signal.aborted) abortQueue();
206
300
  return {
207
- async queue(text, options) {
301
+ async queue(text, options, assertCurrent = () => {}) {
208
302
  try {
209
303
  assertActive();
304
+ assertCurrent();
210
305
  } catch (error) {
211
306
  options?.onQueueAccepted?.(false);
212
307
  throw error;
213
308
  }
214
- const { item, delivery } = createPendingMessage(text, options);
309
+ const { item, delivery } = createPendingMessage(text, options, assertCurrent);
215
310
  batchedMessages.push(item);
216
311
  clearBatchTimer();
217
312
  const debounceMs = normalizeCodexSteerDebounceMs(options?.debounceMs);
@@ -226,21 +321,8 @@ function createCodexSteeringQueue(params) {
226
321
  const batch = dispatchedBatches.get(clientUserMessageId);
227
322
  if (!batch) return false;
228
323
  dispatchedBatches.delete(clientUserMessageId);
229
- for (const item of batch.items) reportItemAcceptance(item, true);
230
- const resolveBatch = () => {
231
- for (const item of batch.items) resolveItem(item);
232
- return true;
233
- };
234
- const rejectBatch = (error) => {
235
- for (const item of batch.items) rejectItem(item, error);
236
- return true;
237
- };
238
- if (!params.beforeConfirmConsumed) return resolveBatch();
239
- try {
240
- return params.beforeConfirmConsumed(batch.items).then(resolveBatch, rejectBatch);
241
- } catch (error) {
242
- return rejectBatch(error);
243
- }
324
+ for (const item of batch.items) resolveItem(item);
325
+ return true;
244
326
  },
245
327
  sealAdmission: sealQueueAdmission,
246
328
  cancel: cancelQueue
@@ -251,6 +333,9 @@ function normalizeCodexSteerDebounceMs(value) {
251
333
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : CODEX_STEER_ALL_DEBOUNCE_MS;
252
334
  }
253
335
  //#endregion
336
+ //#region extensions/codex/src/app-server/attempt-terminal.ts
337
+ const attemptTerminal = agentHarnessAttemptTerminal;
338
+ //#endregion
254
339
  //#region extensions/codex/src/app-server/event-projector-assistant.ts
255
340
  var CodexAssistantProjection = class {
256
341
  constructor(params, emitAgentEvent, matchesToolProgressEcho, nextTranscriptTimestamp, checkpointCommentary) {
@@ -320,6 +405,7 @@ var CodexAssistantProjection = class {
320
405
  this.emitAgentEvent({
321
406
  stream: "assistant",
322
407
  data: {
408
+ itemId,
323
409
  ...streamPayload,
324
410
  ...replaceable ? { replaceable: true } : {}
325
411
  }
@@ -1000,8 +1086,61 @@ function reasoningMethodOrder(method) {
1000
1086
  return method === "item/reasoning/summaryTextDelta" ? 0 : 1;
1001
1087
  }
1002
1088
  //#endregion
1003
- //#region extensions/codex/src/app-server/attempt-terminal.ts
1004
- const attemptTerminal = agentHarnessAttemptTerminal;
1089
+ //#region extensions/codex/src/app-server/event-projector-settlement.ts
1090
+ var CodexProjectionSettlement = class {
1091
+ constructor(params, isActive) {
1092
+ this.turnTainted = false;
1093
+ this.pending = /* @__PURE__ */ new Map();
1094
+ const detach = (stage, callback) => callback ? (...args) => {
1095
+ if (!isActive()) return;
1096
+ const completion = createDeferred();
1097
+ this.pending.set(completion.promise, stage);
1098
+ const settled = () => {
1099
+ this.pending.delete(completion.promise);
1100
+ completion.resolve();
1101
+ };
1102
+ const failed = (error) => {
1103
+ embeddedAgentLog.warn(`codex app-server ${stage} callback failed: ${String(error)}`);
1104
+ settled();
1105
+ };
1106
+ try {
1107
+ Promise.resolve(callback(...args)).then(settled, failed);
1108
+ } catch (error) {
1109
+ failed(error);
1110
+ }
1111
+ } : void 0;
1112
+ this.params = {
1113
+ ...params,
1114
+ onAssistantMessageStart: detach("onAssistantMessageStart", params.onAssistantMessageStart),
1115
+ onPartialReply: detach("onPartialReply", params.onPartialReply),
1116
+ onReasoningStream: detach("onReasoningStream", params.onReasoningStream),
1117
+ onReasoningEnd: detach("onReasoningEnd", params.onReasoningEnd)
1118
+ };
1119
+ }
1120
+ get pendingStage() {
1121
+ return this.stage ?? this.pending.values().next().value;
1122
+ }
1123
+ get completedAnswer() {
1124
+ const turn = this.terminalReceipt;
1125
+ const answer = turn?.items?.findLast((item) => item.type === "agentMessage" && item.phase !== "commentary" && item.delivery !== "async" && typeof item.text === "string" && item.text.trim().length > 0);
1126
+ return turn?.status === "completed" && answer ? {
1127
+ turn,
1128
+ answer
1129
+ } : void 0;
1130
+ }
1131
+ async project(stage, project) {
1132
+ const previous = this.stage;
1133
+ this.stage = stage;
1134
+ try {
1135
+ return await project();
1136
+ } finally {
1137
+ this.stage = previous;
1138
+ }
1139
+ }
1140
+ async drain() {
1141
+ while (this.pending.size > 0) await Promise.all(this.pending.keys());
1142
+ }
1143
+ };
1005
1144
  //#endregion
1006
1145
  //#region extensions/codex/src/app-server/event-projector-snapshot.ts
1007
1146
  function buildCodexMessagesSnapshot(params) {
@@ -1016,7 +1155,10 @@ function buildCodexMessagesSnapshot(params) {
1016
1155
  ...params.toolMessages
1017
1156
  ].toSorted((left, right) => (asDateTimestampMs(left.timestamp) ?? 0) - (asDateTimestampMs(right.timestamp) ?? 0));
1018
1157
  messages.push(...visibleWorkMessages);
1019
- if (params.lastAssistant) messages.push(attachCodexMirrorIdentity(params.lastAssistant, `${params.turnId}:assistant`));
1158
+ if (params.lastAssistant) {
1159
+ const assistant = applyCodexTranscriptTaint(params.lastAssistant, { tainted: params.turnTainted === true });
1160
+ messages.push(attachCodexMirrorIdentity(assistant, `${params.turnId}:assistant`));
1161
+ }
1020
1162
  const taint = { tainted: false };
1021
1163
  return messages.map((message) => projectAgentHarnessTranscriptMessageForDisplay({
1022
1164
  hidden: params.runParams.trigger === "memory",
@@ -1047,202 +1189,39 @@ function buildCodexSteeringMessagesSnapshot(params) {
1047
1189
  };
1048
1190
  }
1049
1191
  //#endregion
1050
- //#region extensions/codex/src/app-server/event-projector-result.ts
1051
- function buildCodexAttemptResult(input) {
1052
- input.nativeToolLifecycleProjection.finalizeActive();
1053
- const assistantTexts = input.assistantProjection.collectAssistantTexts();
1054
- const asyncMessages = input.assistantProjection.collectAsyncMessages();
1055
- const commentaryMessages = input.assistantProjection.collectCommentaryMessages();
1056
- const reasoningText = input.reasoningProjection.reasoningText();
1057
- const planText = input.reasoningProjection.planText();
1058
- const unavailableThreadUsage = input.tokenUsage ? {
1059
- ...input.tokenUsage,
1060
- contextUsage: { state: "unavailable" }
1061
- } : void 0;
1062
- const completedUsage = input.responseCompletions.usage ?? (input.responseCompletions.modelIterations > 0 ? unavailableThreadUsage : input.tokenUsage);
1063
- const projectedUsage = input.aborted ? unavailableThreadUsage : completedUsage;
1064
- const hasAssistantItemText = input.assistantProjection.hasAssistantItemTextForSynthesis();
1065
- const legacyFailClosed = !input.completedTurn || input.completedTurn.status !== "completed" || hasAssistantItemText;
1066
- const hasDeliverableAssistantOnCompletedTurn = input.completedTurn?.status === "completed" && assistantTexts.some((text) => text.trim().length > 0);
1067
- const synthesizedMissingToolResultError = input.toolTranscriptProjection.synthesizeMissingToolResults({
1068
- synthesize: legacyFailClosed,
1069
- terminalDisposition: input.aborted ? "tool_error" : hasDeliverableAssistantOnCompletedTurn ? "diagnostic_only" : "prompt_error"
1070
- });
1071
- const storedMissingToolResultError = synthesizedMissingToolResultError ?? input.synthesizedMissingToolResultError;
1072
- let promptErrorSource = input.promptErrorSource;
1073
- if (synthesizedMissingToolResultError) {
1074
- input.recordSynthesizedMissingToolResultError(synthesizedMissingToolResultError);
1075
- promptErrorSource = promptErrorSource ?? "prompt";
1076
- }
1077
- const assistantMessageOptions = {
1078
- tokenUsage: projectedUsage,
1079
- aborted: input.aborted,
1080
- promptError: input.promptError
1081
- };
1082
- const lastAssistant = assistantTexts.length ? input.assistantProjection.createAssistantMessage(assistantTexts.join("\n\n"), assistantMessageOptions) : void 0;
1083
- const currentAttemptAssistant = input.assistantProjection.createCurrentAttemptAssistantMessage(assistantMessageOptions);
1084
- const messagesSnapshot = buildCodexMessagesSnapshot({
1085
- runParams: input.runParams,
1086
- turnId: input.turnId,
1087
- upstreamUserText: input.upstreamUserText,
1088
- reasoningText,
1089
- asyncMessages,
1090
- commentaryMessages,
1091
- toolMessages: input.toolTranscriptProjection.transcriptMessages,
1092
- lastAssistant
1093
- });
1094
- const turnFailed = input.completedTurn?.status === "failed";
1095
- const promptError = input.promptError ?? storedMissingToolResultError ?? (turnFailed ? input.completedTurn?.error?.message ?? "codex app-server turn failed" : null);
1096
- const agentHarnessResultClassification = classifyAgentHarnessTerminalOutcome({
1097
- assistantTexts,
1098
- reasoningText,
1099
- planText,
1100
- promptError,
1101
- turnCompleted: Boolean(input.completedTurn)
1102
- });
1103
- const toolMetas = input.toolProgressProjection.toolMetas;
1104
- const hadPotentialSideEffects = input.toolTelemetry.didSendViaMessagingTool || Boolean(input.toolTelemetry.successfulCronAdds || input.toolTelemetry.acceptedSessionSpawns?.length) || input.generatedMediaProjection.hasGeneratedMedia() || input.toolProgressProjection.hasPotentialSideEffects;
1105
- const sentMediaUrls = new Set(input.toolTelemetry.messagingToolSentMediaUrls.map((url) => url.trim()));
1106
- const toolAutoDeliveryMediaUrls = input.toolTelemetry.toolAutoDeliveryMediaUrls?.filter((url) => !sentMediaUrls.has(url.trim()));
1107
- const result = {
1108
- terminal: attemptTerminal.normalize({
1109
- aborted: input.aborted,
1110
- promptError,
1111
- promptErrorSource: promptError ? promptErrorSource || "prompt" : null
1112
- }),
1113
- sessionIdUsed: input.runParams.sessionId,
1114
- terminalTurnId: input.turnId,
1115
- ...agentHarnessResultClassification ? { agentHarnessResultClassification } : {},
1116
- bootstrapPromptWarningSignaturesSeen: input.runParams.bootstrapPromptWarningSignaturesSeen,
1117
- bootstrapPromptWarningSignature: input.runParams.bootstrapPromptWarningSignature,
1118
- ...input.responseCompletions.modelIterations > 0 ? { modelIterations: input.responseCompletions.modelIterations } : {},
1119
- messagesSnapshot,
1120
- assistantTexts,
1121
- toolMetas,
1122
- lastAssistant,
1123
- currentAttemptAssistant,
1124
- ...input.toolProgressProjection.lastToolError ? { lastToolError: input.toolProgressProjection.lastToolError } : {},
1125
- didSendViaMessagingTool: input.toolTelemetry.didSendViaMessagingTool,
1126
- didDeliverSourceReplyViaMessageTool: input.toolTelemetry.didDeliverSourceReplyViaMessageTool === true,
1127
- messagingToolSentTexts: input.toolTelemetry.messagingToolSentTexts,
1128
- messagingToolSentMediaUrls: input.toolTelemetry.messagingToolSentMediaUrls,
1129
- messagingToolSentTargets: input.toolTelemetry.messagingToolSentTargets,
1130
- messagingToolSourceReplyPayloads: input.toolTelemetry.messagingToolSourceReplyPayloads ?? [],
1131
- heartbeatToolResponse: input.toolTelemetry.heartbeatToolResponse,
1132
- toolMediaUrls: input.generatedMediaProjection.buildToolMediaUrls(input.toolTelemetry),
1133
- hostOwnedToolMediaUrls: input.generatedMediaProjection.buildHostOwnedMediaUrls(input.toolTelemetry),
1134
- toolAudioAsVoice: input.toolTelemetry.toolAudioAsVoice,
1135
- successfulCronAdds: input.toolTelemetry.successfulCronAdds,
1136
- acceptedSessionSpawns: input.toolTelemetry.acceptedSessionSpawns,
1137
- cloudCodeAssistFormatError: false,
1138
- contextTokens: input.contextTokens,
1139
- contextTokensSource: input.contextTokensSource,
1140
- attemptUsage: projectedUsage,
1141
- ...input.completedCompactionCount > 0 ? { compactionCount: input.completedCompactionCount } : {},
1142
- replayMetadata: {
1143
- hadPotentialSideEffects,
1144
- replaySafe: !hadPotentialSideEffects
1145
- },
1146
- itemLifecycle: {
1147
- startedCount: input.activeItemCount + input.completedItemCount,
1148
- completedCount: input.completedItemCount,
1149
- activeCount: input.activeItemCount
1150
- },
1151
- yieldDetected: input.yieldDetected || false,
1152
- didSendDeterministicApprovalPrompt: input.guardianReviewCount > 0 ? false : void 0
1153
- };
1154
- const transferTtsProvenance = resolveCodexTtsProvenanceTransfer(input.runParams.hostCapabilities);
1155
- for (const toolResult of input.toolTelemetry.coreTtsToolResults ?? []) transferTtsProvenance?.(toolResult, result, toolAutoDeliveryMediaUrls ?? []);
1156
- return result;
1157
- }
1158
- //#endregion
1159
- //#region extensions/codex/src/app-server/transcript-checkpoint.ts
1160
- /** Commits completed work in receipt order without waiting for the enclosing turn. */
1161
- var CodexTranscriptCheckpoint = class {
1162
- constructor(params, threadId, turnId) {
1163
- this.params = params;
1164
- this.threadId = threadId;
1165
- this.turnId = turnId;
1166
- this.pending = [];
1167
- this.commentaryItemIds = /* @__PURE__ */ new Set();
1168
- this.lastTimestamp = 0;
1169
- this.writing = Promise.resolve();
1170
- this.tainted = false;
1171
- this.closed = false;
1172
- this.nextTimestamp = () => {
1173
- this.lastTimestamp = Math.max(Date.now(), this.lastTimestamp + 1);
1174
- return this.lastTimestamp;
1175
- };
1176
- this.enqueueCommentary = (itemId, entry) => {
1177
- if (this.params.config?.ui?.prefs?.chatPersistCommentary === false || this.commentaryItemIds.has(itemId)) return;
1178
- this.commentaryItemIds.add(itemId);
1179
- this.enqueue({
1180
- ...entry,
1181
- read: () => {
1182
- const message = entry.read();
1183
- return message ? attachCodexMirrorIdentity(message, `${this.turnId}:commentary:${itemId}`) : void 0;
1184
- }
1185
- });
1186
- };
1187
- this.enqueue = (entry) => {
1188
- if (this.params.sessionTarget && !this.closed) this.pending.push(entry);
1189
- };
1190
- }
1191
- flush(close = false) {
1192
- if (this.closed) return this.writing;
1193
- this.closed = close;
1194
- this.writing = this.writing.then(async () => {
1195
- const blocked = close ? -1 : this.pending.findIndex((entry) => entry.ready?.() === false);
1196
- const count = blocked < 0 ? this.pending.length : blocked;
1197
- if (count === 0) return;
1198
- const taint = { tainted: this.tainted };
1199
- const messages = this.pending.slice(0, count).flatMap((entry) => {
1200
- const message = entry.read();
1201
- return message ? [projectAgentHarnessTranscriptMessageForDisplay({
1202
- hidden: this.params.trigger === "memory",
1203
- message: applyCodexTranscriptTaint(message, taint)
1204
- })] : [];
1205
- });
1206
- try {
1207
- await codexTranscriptMirrorRuntime.mirror({
1208
- ...this.params.sessionTarget,
1209
- sessionId: this.params.sessionId,
1210
- cwd: this.params.workspaceDir,
1211
- messages,
1212
- idempotencyScope: `codex-app-server:${this.threadId}`,
1213
- runId: this.params.runId,
1214
- runMirrorIdentityPrefix: `${this.turnId}:`,
1215
- config: this.params.config
1216
- });
1217
- this.pending.splice(0, count);
1218
- this.tainted = taint.tainted;
1219
- } catch (error) {
1220
- embeddedAgentLog.warn("failed to checkpoint codex app-server transcript", {
1221
- runId: this.params.runId,
1222
- sessionId: this.params.sessionId,
1223
- error: formatErrorMessage(error)
1224
- });
1225
- }
1226
- });
1227
- return this.writing;
1228
- }
1229
- };
1230
- //#endregion
1231
1192
  //#region extensions/codex/src/app-server/usage-limit-error.ts
1232
1193
  /**
1233
1194
  * Enriches Codex usage-limit failures with current rate-limit information and
1234
1195
  * marks blocked auth profiles when Codex exposes a reset time.
1235
1196
  */
1236
1197
  const CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS = 5e3;
1237
- function createCodexUsageLimitPromptError(message) {
1238
- return Object.assign(new Error(message), { status: 429 });
1239
- }
1198
+ var CodexUsageLimitPromptError = class extends Error {
1199
+ constructor(..._args) {
1200
+ super(..._args);
1201
+ this.status = 429;
1202
+ }
1203
+ };
1240
1204
  function resolveCodexPromptError(source) {
1241
1205
  const usageLimitMessage = formatCodexUsageLimitErrorMessage(source);
1242
- return usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : source.message ?? void 0;
1243
- }
1244
- function isCodexUsageLimitPromptError(error) {
1245
- return error instanceof Error && "status" in error && error.status === 429;
1206
+ if (usageLimitMessage) return new CodexUsageLimitPromptError(usageLimitMessage);
1207
+ const info = source.codexErrorInfo;
1208
+ let status = info === "rateLimitExceeded" ? 429 : info === "serverOverloaded" ? 503 : info === "internalServerError" ? 500 : void 0;
1209
+ if (isJsonObject(info)) for (const variant of [
1210
+ "httpConnectionFailed",
1211
+ "responseStreamConnectionFailed",
1212
+ "responseStreamDisconnected",
1213
+ "responseTooManyFailedAttempts"
1214
+ ]) {
1215
+ const detail = info[variant];
1216
+ if (isJsonObject(detail) && typeof detail.httpStatusCode === "number") {
1217
+ status = detail.httpStatusCode;
1218
+ break;
1219
+ }
1220
+ }
1221
+ return status === void 0 ? source.message ?? void 0 : Object.assign(new Error(source.message ?? "codex app-server error"), {
1222
+ status,
1223
+ ...info === "serverOverloaded" ? { code: "OVERLOADED" } : {}
1224
+ });
1246
1225
  }
1247
1226
  /** Marks a Codex auth profile blocked until the reset time advertised by rate limits. */
1248
1227
  async function markCodexAuthProfileBlockedFromRateLimits(params) {
@@ -1368,8 +1347,106 @@ function readCodexErrorPayload(error) {
1368
1347
  };
1369
1348
  }
1370
1349
  //#endregion
1371
- //#region extensions/codex/src/app-server/event-projector.ts
1372
- var CodexAppServerEventProjector = class {
1350
+ //#region extensions/codex/src/app-server/event-projector-terminal-failure.ts
1351
+ var CodexTerminalFailureProjection = class {
1352
+ constructor() {
1353
+ this.promptErrorSource = null;
1354
+ }
1355
+ record(params) {
1356
+ this.providerRefusal ??= readCodexProviderRefusal(params.message, params.codexErrorInfo);
1357
+ if (this.providerRefusal) return;
1358
+ this.promptError = resolveCodexPromptError({
1359
+ message: params.message,
1360
+ codexErrorInfo: params.codexErrorInfo,
1361
+ rateLimits: params.rateLimits
1362
+ }) ?? params.fallbackMessage;
1363
+ this.promptErrorSource = params.promptErrorSource;
1364
+ }
1365
+ };
1366
+ //#endregion
1367
+ //#region extensions/codex/src/app-server/transcript-checkpoint.ts
1368
+ /** Commits completed work in receipt order without waiting for the enclosing turn. */
1369
+ var CodexTranscriptCheckpoint = class {
1370
+ constructor(params, threadId, turnId) {
1371
+ this.params = params;
1372
+ this.threadId = threadId;
1373
+ this.turnId = turnId;
1374
+ this.pending = [];
1375
+ this.commentaryItemIds = /* @__PURE__ */ new Set();
1376
+ this.lastTimestamp = 0;
1377
+ this.writing = Promise.resolve();
1378
+ this.tainted = false;
1379
+ this.closed = false;
1380
+ this.abandoned = false;
1381
+ this.nextTimestamp = () => {
1382
+ this.lastTimestamp = Math.max(Date.now(), this.lastTimestamp + 1);
1383
+ return this.lastTimestamp;
1384
+ };
1385
+ this.enqueueCommentary = (itemId, entry) => {
1386
+ if (this.params.config?.ui?.prefs?.chatPersistCommentary === false || this.commentaryItemIds.has(itemId)) return;
1387
+ this.commentaryItemIds.add(itemId);
1388
+ this.enqueue({
1389
+ ...entry,
1390
+ read: () => {
1391
+ const message = entry.read();
1392
+ return message ? attachCodexMirrorIdentity(message, `${this.turnId}:commentary:${itemId}`) : void 0;
1393
+ }
1394
+ });
1395
+ };
1396
+ this.enqueue = (entry) => {
1397
+ if (this.params.sessionTarget && !this.closed) this.pending.push(entry);
1398
+ };
1399
+ }
1400
+ abandon() {
1401
+ this.closed = this.abandoned = true;
1402
+ this.pending.length = 0;
1403
+ }
1404
+ flush(close = false) {
1405
+ if (this.closed) return this.writing;
1406
+ this.closed = close;
1407
+ this.writing = this.writing.then(async () => {
1408
+ const blocked = close ? -1 : this.pending.findIndex((entry) => entry.ready?.() === false);
1409
+ const count = blocked < 0 ? this.pending.length : blocked;
1410
+ if (count === 0) return;
1411
+ const taint = { tainted: this.tainted };
1412
+ const messages = this.pending.slice(0, count).flatMap((entry) => {
1413
+ const message = entry.read();
1414
+ return message ? [projectAgentHarnessTranscriptMessageForDisplay({
1415
+ hidden: this.params.trigger === "memory",
1416
+ message: applyCodexTranscriptTaint(message, taint)
1417
+ })] : [];
1418
+ });
1419
+ try {
1420
+ await codexTranscriptMirrorRuntime.mirror({
1421
+ assertWriteCurrent: () => {
1422
+ if (this.abandoned) throw new Error("Codex transcript checkpoint was retired before write");
1423
+ },
1424
+ ...this.params.sessionTarget,
1425
+ sessionId: this.params.sessionId,
1426
+ cwd: this.params.workspaceDir,
1427
+ messages,
1428
+ idempotencyScope: `codex-app-server:${this.threadId}`,
1429
+ runId: this.params.runId,
1430
+ runMirrorIdentityPrefix: `${this.turnId}:`,
1431
+ config: this.params.config
1432
+ });
1433
+ this.pending.splice(0, count);
1434
+ this.tainted = taint.tainted;
1435
+ } catch (error) {
1436
+ embeddedAgentLog.warn("failed to checkpoint codex app-server transcript", {
1437
+ runId: this.params.runId,
1438
+ sessionId: this.params.sessionId,
1439
+ error: formatErrorMessage(error)
1440
+ });
1441
+ }
1442
+ });
1443
+ return this.writing;
1444
+ }
1445
+ };
1446
+ //#endregion
1447
+ //#region extensions/codex/src/app-server/event-projector-result.ts
1448
+ /** Owns per-turn projection state and builds results from the same state. */
1449
+ var CodexTurnProjection = class {
1373
1450
  constructor(params, threadId, turnId, options = {}) {
1374
1451
  this.params = params;
1375
1452
  this.threadId = threadId;
@@ -1378,15 +1455,14 @@ var CodexAppServerEventProjector = class {
1378
1455
  this.activeItemIds = /* @__PURE__ */ new Set();
1379
1456
  this.completedItemIds = /* @__PURE__ */ new Set();
1380
1457
  this.activeCompactionItemIds = /* @__PURE__ */ new Set();
1381
- this.terminalPresentationClearedItemIds = /* @__PURE__ */ new Set();
1382
- this.nativeToolOutcomeOrdinals = /* @__PURE__ */ new Map();
1383
- this.projectionClosed = false;
1458
+ this.projectionController = new AbortController();
1384
1459
  this.settledTurnFailureFinalizationAllowed = false;
1385
- this.promptErrorSource = null;
1460
+ this.terminalFailure = new CodexTerminalFailureProjection();
1386
1461
  this.synthesizedMissingToolResultError = null;
1387
1462
  this.aborted = false;
1388
- this.responseCompletions = new CodexResponseCompletionProjection();
1463
+ this.usageProjection = new CodexUsageProjection();
1389
1464
  this.completedCompactionCount = 0;
1465
+ this.settlement = new CodexProjectionSettlement(params, () => !this.projectionClosed);
1390
1466
  this.transcriptCheckpoint = new CodexTranscriptCheckpoint(params, threadId, turnId);
1391
1467
  this.asyncDeliveryProjection = new CodexAsyncDeliveryProjection(params, threadId, turnId, options);
1392
1468
  this.contextTokens = options.initialContextTokens;
@@ -1397,7 +1473,7 @@ var CodexAppServerEventProjector = class {
1397
1473
  remoteWorkspaceRoot: options.remoteWorkspaceRoot,
1398
1474
  readFile: options.readRemoteWorkspaceFile,
1399
1475
  requestTimeoutMs: options.remoteWorkspaceRequestTimeoutMs,
1400
- signal: options.runAbortSignal
1476
+ signal: options.runAbortSignal ? AbortSignal.any([options.runAbortSignal, this.projectionController.signal]) : this.projectionController.signal
1401
1477
  });
1402
1478
  this.toolProgressProjection = new CodexToolProgressProjection(params);
1403
1479
  this.toolTranscriptProjection = new CodexToolTranscriptProjection(params, threadId, turnId, this.toolProgressProjection, this.transcriptCheckpoint.nextTimestamp, {
@@ -1407,12 +1483,151 @@ var CodexAppServerEventProjector = class {
1407
1483
  checkpointMessage: this.transcriptCheckpoint.enqueue
1408
1484
  });
1409
1485
  this.eventProjection = new CodexEventProjection(threadId, turnId, (event) => this.emitAgentEvent(event), this.toolProgressProjection, this.toolTranscriptProjection, options.onNativeToolResultRecorded);
1410
- this.assistantProjection = new CodexAssistantProjection(params, (event) => this.emitAgentEvent(event), (text) => this.toolProgressProjection.matchesEcho(text), this.transcriptCheckpoint.nextTimestamp, this.transcriptCheckpoint.enqueueCommentary);
1411
- this.reasoningProjection = new CodexReasoningProjection(params, (event) => this.emitAgentEvent(event), options.onNativePlanUpdate);
1486
+ this.assistantProjection = new CodexAssistantProjection(this.settlement.params, (event) => this.emitAgentEvent(event), (text) => this.toolProgressProjection.matchesEcho(text), this.transcriptCheckpoint.nextTimestamp, this.transcriptCheckpoint.enqueueCommentary);
1487
+ this.reasoningProjection = new CodexReasoningProjection(this.settlement.params, (event) => this.emitAgentEvent(event), options.onNativePlanUpdate);
1412
1488
  }
1489
+ get projectionClosed() {
1490
+ return this.projectionController.signal.aborted;
1491
+ }
1492
+ buildResult(toolTelemetry, options) {
1493
+ this.eventProjection.flushPendingGuardianWarning();
1494
+ const { params: runParams, turnId, completedTurn, aborted, contextTokens, contextTokensSource, completedCompactionCount, synthesizedMissingToolResultError: previousMissingToolResultError } = this;
1495
+ const { promptError: initialPromptError, promptErrorSource: initialPromptErrorSource, providerRefusal } = this.terminalFailure;
1496
+ const upstreamUserText = this.options.upstreamUserText;
1497
+ const turnTainted = this.settlement.turnTainted;
1498
+ const activeItemCount = this.activeItemIds.size;
1499
+ const completedItemCount = this.completedItemIds.size;
1500
+ const guardianReviewCount = this.eventProjection.guardianReviewCount;
1501
+ const yieldDetected = options?.yieldDetected;
1502
+ this.nativeToolLifecycleProjector.finalizeActive();
1503
+ const assistantTexts = this.assistantProjection.collectAssistantTexts();
1504
+ const asyncMessages = this.assistantProjection.collectAsyncMessages();
1505
+ const commentaryMessages = this.assistantProjection.collectCommentaryMessages();
1506
+ const reasoningText = this.reasoningProjection.reasoningText();
1507
+ const planText = this.reasoningProjection.planText();
1508
+ const usage = this.usageProjection.usage;
1509
+ const projectedUsage = aborted && usage ? {
1510
+ ...usage,
1511
+ contextUsage: { state: "unavailable" }
1512
+ } : usage;
1513
+ const hasAssistantItemText = this.assistantProjection.hasAssistantItemTextForSynthesis();
1514
+ const legacyFailClosed = !completedTurn || completedTurn.status !== "completed" || hasAssistantItemText;
1515
+ const hasDeliverableAssistantOnCompletedTurn = completedTurn?.status === "completed" && assistantTexts.some((text) => text.trim().length > 0);
1516
+ const synthesizedMissingToolResultError = this.toolTranscriptProjection.synthesizeMissingToolResults({
1517
+ synthesize: legacyFailClosed,
1518
+ terminalDisposition: aborted ? "tool_error" : hasDeliverableAssistantOnCompletedTurn ? "diagnostic_only" : "prompt_error"
1519
+ });
1520
+ const storedMissingToolResultError = synthesizedMissingToolResultError ?? previousMissingToolResultError;
1521
+ let promptErrorSource = initialPromptErrorSource;
1522
+ if (synthesizedMissingToolResultError) {
1523
+ this.synthesizedMissingToolResultError = synthesizedMissingToolResultError;
1524
+ promptErrorSource = promptErrorSource ?? "prompt";
1525
+ }
1526
+ const assistantMessageOptions = {
1527
+ tokenUsage: projectedUsage,
1528
+ aborted,
1529
+ promptError: initialPromptError,
1530
+ providerRefusal
1531
+ };
1532
+ const lastAssistant = providerRefusal ? this.assistantProjection.createAssistantMessage("", assistantMessageOptions) : assistantTexts.length ? this.assistantProjection.createAssistantMessage(assistantTexts.join("\n\n"), assistantMessageOptions) : void 0;
1533
+ const currentAttemptAssistant = providerRefusal ? lastAssistant : this.assistantProjection.createCurrentAttemptAssistantMessage(assistantMessageOptions);
1534
+ const messagesSnapshot = buildCodexMessagesSnapshot({
1535
+ runParams,
1536
+ turnId,
1537
+ upstreamUserText,
1538
+ reasoningText,
1539
+ asyncMessages,
1540
+ commentaryMessages,
1541
+ toolMessages: this.toolTranscriptProjection.transcriptMessages,
1542
+ lastAssistant,
1543
+ turnTainted
1544
+ });
1545
+ const turnFailed = completedTurn?.status === "failed";
1546
+ const promptError = providerRefusal ? null : initialPromptError ?? storedMissingToolResultError ?? (turnFailed ? completedTurn?.error?.message ?? "codex app-server turn failed" : null);
1547
+ const agentHarnessResultClassification = providerRefusal ? void 0 : classifyAgentHarnessTerminalOutcome({
1548
+ assistantTexts,
1549
+ reasoningText,
1550
+ planText,
1551
+ promptError,
1552
+ turnCompleted: Boolean(completedTurn)
1553
+ });
1554
+ const toolMetas = this.toolProgressProjection.toolMetas;
1555
+ const hadPotentialSideEffects = toolTelemetry.didSendViaMessagingTool || Boolean(toolTelemetry.successfulCronAdds || toolTelemetry.acceptedSessionSpawns?.length) || this.generatedMediaProjection.hasGeneratedMedia() || this.toolProgressProjection.hasPotentialSideEffects;
1556
+ const sentMediaUrls = new Set(toolTelemetry.messagingToolSentMediaUrls.map((url) => url.trim()));
1557
+ const toolAutoDeliveryMediaUrls = toolTelemetry.toolAutoDeliveryMediaUrls?.filter((url) => !sentMediaUrls.has(url.trim()));
1558
+ const result = {
1559
+ terminal: attemptTerminal.normalize({
1560
+ aborted,
1561
+ promptError,
1562
+ promptErrorSource: promptError ? promptErrorSource || "prompt" : null
1563
+ }),
1564
+ sessionIdUsed: runParams.sessionId,
1565
+ terminalTurnId: turnId,
1566
+ ...agentHarnessResultClassification ? { agentHarnessResultClassification } : {},
1567
+ bootstrapPromptWarningSignaturesSeen: runParams.bootstrapPromptWarningSignaturesSeen,
1568
+ bootstrapPromptWarningSignature: runParams.bootstrapPromptWarningSignature,
1569
+ ...this.usageProjection.modelIterations > 0 ? { modelIterations: this.usageProjection.modelIterations } : {},
1570
+ messagesSnapshot,
1571
+ assistantTexts,
1572
+ toolMetas,
1573
+ lastAssistant,
1574
+ currentAttemptAssistant,
1575
+ ...this.toolProgressProjection.lastToolError ? { lastToolError: this.toolProgressProjection.lastToolError } : {},
1576
+ didSendViaMessagingTool: toolTelemetry.didSendViaMessagingTool,
1577
+ didDeliverSourceReplyViaMessageTool: toolTelemetry.didDeliverSourceReplyViaMessageTool === true,
1578
+ sourceReplyDelivered: toolTelemetry.sourceReplyDelivered,
1579
+ messagingToolSentTexts: toolTelemetry.messagingToolSentTexts,
1580
+ messagingToolSentMediaUrls: toolTelemetry.messagingToolSentMediaUrls,
1581
+ messagingToolSentTargets: toolTelemetry.messagingToolSentTargets,
1582
+ messagingToolSourceReplyPayloads: toolTelemetry.messagingToolSourceReplyPayloads ?? [],
1583
+ heartbeatToolResponse: toolTelemetry.heartbeatToolResponse,
1584
+ toolMediaUrls: this.generatedMediaProjection.buildToolMediaUrls(toolTelemetry),
1585
+ hostOwnedToolMediaUrls: this.generatedMediaProjection.buildHostOwnedMediaUrls(toolTelemetry),
1586
+ toolAudioAsVoice: toolTelemetry.toolAudioAsVoice,
1587
+ successfulCronAdds: toolTelemetry.successfulCronAdds,
1588
+ acceptedSessionSpawns: toolTelemetry.acceptedSessionSpawns,
1589
+ cloudCodeAssistFormatError: false,
1590
+ contextTokens,
1591
+ contextTokensSource,
1592
+ attemptUsage: projectedUsage,
1593
+ ...completedCompactionCount > 0 ? { compactionCount: completedCompactionCount } : {},
1594
+ replayMetadata: {
1595
+ hadPotentialSideEffects,
1596
+ replaySafe: !hadPotentialSideEffects
1597
+ },
1598
+ itemLifecycle: {
1599
+ startedCount: activeItemCount + completedItemCount,
1600
+ completedCount: completedItemCount,
1601
+ activeCount: activeItemCount
1602
+ },
1603
+ yieldDetected: yieldDetected || false,
1604
+ didSendDeterministicApprovalPrompt: guardianReviewCount > 0 ? false : void 0
1605
+ };
1606
+ const transferTtsProvenance = resolveCodexTtsProvenanceTransfer(runParams.hostCapabilities);
1607
+ for (const toolResult of toolTelemetry.coreTtsToolResults ?? []) transferTtsProvenance?.(toolResult, result, toolAutoDeliveryMediaUrls ?? []);
1608
+ return result;
1609
+ }
1610
+ emitAgentEvent(event) {
1611
+ if (!this.projectionClosed) emitCodexAgentEvent(this.params, event);
1612
+ }
1613
+ };
1614
+ //#endregion
1615
+ //#region extensions/codex/src/app-server/event-projector.ts
1616
+ var CodexAppServerEventProjector = class extends CodexTurnProjection {
1413
1617
  getCompletedTurnStatus() {
1414
1618
  return this.completedTurn?.status;
1415
1619
  }
1620
+ /** Native completion owns the answer independently of unfinished host projection. */
1621
+ recoverCompletedAnswer() {
1622
+ const completed = this.settlement.completedAnswer;
1623
+ if (!completed || this.aborted || this.terminalFailure.promptError) return false;
1624
+ this.projectionController.abort();
1625
+ this.transcriptCheckpoint.abandon();
1626
+ this.completedTurn = completed.turn;
1627
+ this.assistantProjection.recordSnapshotItem(completed.answer);
1628
+ this.assistantProjection.finalizeAnswerCandidate(completed.turn);
1629
+ return true;
1630
+ }
1416
1631
  getActiveMcpToolCall(serverName) {
1417
1632
  if (this.projectionClosed || this.aborted) return;
1418
1633
  return this.nativeToolLifecycleProjector.getActiveMcpToolCall(serverName);
@@ -1441,14 +1656,12 @@ var CodexAppServerEventProjector = class {
1441
1656
  }
1442
1657
  /** Fence delayed projections before the turn's final snapshot leaves its owner. */
1443
1658
  closeProjection() {
1444
- this.projectionClosed = true;
1445
- return this.transcriptCheckpoint.flush(true);
1659
+ this.projectionController.abort();
1660
+ return this.settlement.project("transcript/checkpoint", () => this.transcriptCheckpoint.flush(true));
1446
1661
  }
1447
1662
  /** Resolves the shared model-order position for a native tool item. */
1448
1663
  recordNativeToolOutcome(item) {
1449
- if (!item || this.nativeToolOutcomeOrdinals.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
1450
- const ordinal = this.params.allocateToolOutcomeOrdinal?.(item.id);
1451
- if (ordinal !== void 0) this.nativeToolOutcomeOrdinals.set(item.id, ordinal);
1664
+ this.nativeToolLifecycleProjector.recordNativeToolOutcome(item);
1452
1665
  }
1453
1666
  recordNativeToolApprovalFailure(toolCallId, disposition, approvalKind) {
1454
1667
  this.nativeToolLifecycleProjector.recordApprovalFailureDisposition(toolCallId, disposition);
@@ -1486,11 +1699,9 @@ var CodexAppServerEventProjector = class {
1486
1699
  break;
1487
1700
  case "item/started":
1488
1701
  await this.handleItemStarted(params);
1489
- await this.transcriptCheckpoint.flush();
1490
1702
  break;
1491
1703
  case "item/completed":
1492
1704
  await this.handleItemCompleted(params);
1493
- await this.transcriptCheckpoint.flush();
1494
1705
  break;
1495
1706
  case "item/commandExecution/outputDelta":
1496
1707
  this.toolProgressProjection.handleOutputDelta(params, "bash");
@@ -1513,43 +1724,46 @@ var CodexAppServerEventProjector = class {
1513
1724
  case "hook/completed":
1514
1725
  this.eventProjection.handleHook(notification.method, params);
1515
1726
  break;
1516
- case "thread/tokenUsage/updated":
1517
- projectCodexThreadUsageUpdate(params, this.tokenUsage, (usage) => this.tokenUsage = usage, (data) => {
1518
- if (data.modelContextWindow !== void 0) {
1519
- this.contextTokens = data.modelContextWindow;
1520
- this.contextTokensSource = this.params.authoredContextTokenCap === void 0 ? "runtime" : "runtime-configured";
1521
- }
1522
- this.emitAgentEvent({
1523
- stream: "usage",
1524
- data
1525
- });
1727
+ case "thread/tokenUsage/updated": {
1728
+ const data = this.usageProjection.recordThread(params);
1729
+ if (data.modelContextWindow !== void 0) {
1730
+ this.contextTokens = data.modelContextWindow;
1731
+ this.contextTokensSource = this.params.authoredContextTokenCap === void 0 ? "runtime" : "runtime-configured";
1732
+ }
1733
+ if (Object.keys(data).length > 0) this.emitAgentEvent({
1734
+ stream: "usage",
1735
+ data
1526
1736
  });
1527
1737
  break;
1738
+ }
1528
1739
  case "turn/completed":
1529
1740
  await this.handleTurnCompleted(params);
1530
1741
  break;
1531
1742
  case "rawResponse/completed":
1532
- this.responseCompletions.record(params, this.params.hostCapabilities.reportOutputTokens);
1743
+ this.usageProjection.record(params, this.params.hostCapabilities.reportOutputTokens);
1533
1744
  break;
1534
1745
  case "rawResponseItem/completed":
1535
1746
  await this.handleRawResponseItemCompleted(params);
1536
- await this.transcriptCheckpoint.flush();
1537
1747
  break;
1538
1748
  case "model/rerouted":
1539
1749
  this.eventProjection.handleModelRerouted(params);
1540
1750
  break;
1541
1751
  case "error": {
1542
- this.responseCompletions.clear();
1543
- if (params.willRetry === true) break;
1752
+ this.usageProjection.invalidateContext();
1753
+ if (params.willRetry === true) {
1754
+ this.eventProjection.handleRetry(params);
1755
+ break;
1756
+ }
1544
1757
  const codexErrorInfo = isJsonObject(params.error) ? params.error.codexErrorInfo : void 0;
1545
1758
  const compactionFailure = codexErrorInfo === "other" && this.isCompacting();
1546
1759
  this.settledTurnFailureFinalizationAllowed = codexErrorInfo === "serverOverloaded" || compactionFailure;
1547
- this.promptError = resolveCodexPromptError({
1760
+ this.terminalFailure.record({
1548
1761
  message: readCodexErrorNotificationMessage(params),
1549
1762
  codexErrorInfo,
1550
- rateLimits: this.options.readRecentRateLimits?.()
1551
- }) ?? "codex app-server error";
1552
- this.promptErrorSource = compactionFailure ? "compaction" : "prompt";
1763
+ rateLimits: this.options.readRecentRateLimits?.(),
1764
+ fallbackMessage: "codex app-server error",
1765
+ promptErrorSource: compactionFailure ? "compaction" : "prompt"
1766
+ });
1553
1767
  break;
1554
1768
  }
1555
1769
  case "thread/compacted":
@@ -1565,47 +1779,23 @@ var CodexAppServerEventProjector = class {
1565
1779
  case "model/safetyBuffering/updated": break;
1566
1780
  default: this.diagnostics.warnUnknownEvent(notification, params);
1567
1781
  }
1568
- }
1569
- buildResult(toolTelemetry, options) {
1570
- this.eventProjection.flushPendingGuardianWarning();
1571
- return buildCodexAttemptResult({
1572
- runParams: this.params,
1573
- turnId: this.turnId,
1574
- upstreamUserText: this.options.upstreamUserText,
1575
- completedTurn: this.completedTurn,
1576
- promptError: this.promptError,
1577
- promptErrorSource: this.promptErrorSource,
1578
- synthesizedMissingToolResultError: this.synthesizedMissingToolResultError,
1579
- recordSynthesizedMissingToolResultError: (error) => {
1580
- this.synthesizedMissingToolResultError = error;
1581
- this.promptErrorSource = this.promptErrorSource ?? "prompt";
1582
- },
1583
- aborted: this.aborted,
1584
- tokenUsage: this.tokenUsage,
1585
- contextTokens: this.contextTokens,
1586
- contextTokensSource: this.contextTokensSource,
1587
- completedCompactionCount: this.completedCompactionCount,
1588
- activeItemCount: this.activeItemIds.size,
1589
- completedItemCount: this.completedItemIds.size,
1590
- guardianReviewCount: this.eventProjection.guardianReviewCount,
1591
- toolTelemetry,
1592
- yieldDetected: options?.yieldDetected,
1593
- nativeToolLifecycleProjection: this.nativeToolLifecycleProjector,
1594
- assistantProjection: this.assistantProjection,
1595
- reasoningProjection: this.reasoningProjection,
1596
- responseCompletions: this.responseCompletions,
1597
- toolTranscriptProjection: this.toolTranscriptProjection,
1598
- toolProgressProjection: this.toolProgressProjection,
1599
- generatedMediaProjection: this.generatedMediaProjection
1600
- });
1782
+ if (!this.projectionClosed && [
1783
+ "item/started",
1784
+ "item/completed",
1785
+ "rawResponseItem/completed"
1786
+ ].includes(notification.method)) await this.settlement.project("transcript/checkpoint", () => this.transcriptCheckpoint.flush());
1601
1787
  }
1602
1788
  recordDynamicToolCall(params) {
1603
1789
  this.toolTranscriptProjection.recordDynamicToolCall(params);
1604
1790
  }
1605
1791
  /** Projects a successful OpenClaw progress_card call through the native plan stream. */
1606
1792
  async recordDynamicProgressCardUpdate(params) {
1607
- if (isJsonObject(params)) {
1608
- const projected = { plan: Array.isArray(params.plan) ? params.plan : [] };
1793
+ const update = projectProgressCardChannelUpdate(params);
1794
+ if (update) {
1795
+ const projected = {
1796
+ plan: update.steps,
1797
+ ...update.explanation ? { explanation: update.explanation } : {}
1798
+ };
1609
1799
  await this.reasoningProjection.handleTurnPlanUpdated(projected, "openclaw");
1610
1800
  }
1611
1801
  }
@@ -1616,16 +1806,19 @@ var CodexAppServerEventProjector = class {
1616
1806
  }
1617
1807
  markTimedOut() {
1618
1808
  this.aborted = true;
1619
- this.promptError = "codex app-server attempt timed out";
1620
- this.promptErrorSource = "prompt";
1809
+ this.terminalFailure.promptError = "codex app-server attempt timed out";
1810
+ this.terminalFailure.promptErrorSource = "prompt";
1621
1811
  }
1622
1812
  markAborted() {
1623
1813
  this.aborted = true;
1624
- this.responseCompletions.clear();
1814
+ this.usageProjection.invalidateContext();
1625
1815
  }
1626
1816
  isCompacting() {
1627
1817
  return this.activeCompactionItemIds.size > 0;
1628
1818
  }
1819
+ isCompactionProjectionActive() {
1820
+ return !this.projectionClosed && !this.aborted && !this.options.runAbortSignal?.aborted;
1821
+ }
1629
1822
  async handleItemStarted(params) {
1630
1823
  const item = readItem(params.item);
1631
1824
  const itemId = item?.id ?? readStringField(params, "itemId");
@@ -1633,12 +1826,16 @@ var CodexAppServerEventProjector = class {
1633
1826
  if (itemId) this.activeItemIds.add(itemId);
1634
1827
  this.recordNativeToolOutcome(item);
1635
1828
  if (item?.type === "contextCompaction" && itemId) {
1829
+ if (!this.isCompactionProjectionActive()) return;
1636
1830
  this.activeCompactionItemIds.add(itemId);
1831
+ const messages = await this.toolTranscriptProjection.readMirroredSessionMessages(this.options.runAbortSignal);
1832
+ if (!this.isCompactionProjectionActive()) return;
1637
1833
  await runAgentHarnessBeforeCompactionHook({
1638
1834
  sessionFile: this.params.sessionFile,
1639
- messages: await this.toolTranscriptProjection.readMirroredSessionMessages(),
1835
+ messages,
1640
1836
  ctx: this.options.agentHookContext ?? {}
1641
1837
  });
1838
+ if (!this.isCompactionProjectionActive()) return;
1642
1839
  this.emitAgentEvent({
1643
1840
  stream: "compaction",
1644
1841
  data: {
@@ -1675,7 +1872,7 @@ var CodexAppServerEventProjector = class {
1675
1872
  const item = readItem(params.item);
1676
1873
  this.diagnostics.warnUnknownItemStatus(item);
1677
1874
  this.recordNativeToolOutcome(item);
1678
- this.clearTerminalPresentationForNativeItem(item);
1875
+ this.nativeToolLifecycleProjector.clearTerminalPresentationForNativeItem(item);
1679
1876
  const itemId = item?.id ?? readStringField(params, "itemId");
1680
1877
  if (itemId) {
1681
1878
  this.activeItemIds.delete(itemId);
@@ -1686,20 +1883,23 @@ var CodexAppServerEventProjector = class {
1686
1883
  if (asyncMessage) await this.asyncDeliveryProjection.deliver(asyncMessage);
1687
1884
  if (this.projectionClosed) return;
1688
1885
  this.reasoningProjection.recordItem(item);
1689
- await this.generatedMediaProjection.recordNative(item);
1886
+ await this.settlement.project("media_projection", () => this.generatedMediaProjection.recordNative(item));
1690
1887
  if (this.projectionClosed) return;
1691
1888
  if (item?.type === "contextCompaction" && itemId) {
1889
+ if (!this.isCompactionProjectionActive()) return;
1692
1890
  this.activeCompactionItemIds.delete(itemId);
1693
1891
  this.completedCompactionCount += 1;
1694
1892
  await this.options.onContextCompacted?.();
1695
- if (this.projectionClosed) return;
1893
+ if (!this.isCompactionProjectionActive()) return;
1894
+ const messages = await this.toolTranscriptProjection.readMirroredSessionMessages(this.options.runAbortSignal);
1895
+ if (!this.isCompactionProjectionActive()) return;
1696
1896
  await runAgentHarnessAfterCompactionHook({
1697
1897
  sessionFile: this.params.sessionFile,
1698
- messages: await this.toolTranscriptProjection.readMirroredSessionMessages(),
1898
+ messages,
1699
1899
  compactedCount: -1,
1700
1900
  ctx: this.options.agentHookContext ?? {}
1701
1901
  });
1702
- if (this.projectionClosed) return;
1902
+ if (!this.isCompactionProjectionActive()) return;
1703
1903
  await persistCodexContextCompactionActivity({
1704
1904
  sessionTarget: this.params.sessionTarget,
1705
1905
  config: this.params.config,
@@ -1710,6 +1910,7 @@ var CodexAppServerEventProjector = class {
1710
1910
  itemId,
1711
1911
  timestamp: this.transcriptCheckpoint.nextTimestamp()
1712
1912
  });
1913
+ if (!this.isCompactionProjectionActive()) return;
1713
1914
  this.eventProjection.emitCompactionEnd(itemId, true);
1714
1915
  }
1715
1916
  this.toolProgressProjection.recordToolMeta(item);
@@ -1724,8 +1925,9 @@ var CodexAppServerEventProjector = class {
1724
1925
  });
1725
1926
  if (this.projectionClosed) return;
1726
1927
  this.toolTranscriptProjection.recordNativeToolCall(item);
1727
- await this.toolTranscriptProjection.recordNativeToolResultWithDetails(item);
1928
+ const details = await this.toolTranscriptProjection.prepareNativeToolResultDetails(item);
1728
1929
  if (this.projectionClosed) return;
1930
+ this.toolTranscriptProjection.recordNativeToolResult(item, details);
1729
1931
  this.toolProgressProjection.emitToolResultSummary(item);
1730
1932
  this.toolProgressProjection.emitToolResultOutput(item);
1731
1933
  this.emitAgentEvent({
@@ -1741,16 +1943,18 @@ var CodexAppServerEventProjector = class {
1741
1943
  const turn = readCodexTurn(params.turn);
1742
1944
  if (!turn || turn.id !== this.turnId) return;
1743
1945
  this.completedTurn = turn;
1744
- const compactionFailure = turn.status === "failed" && (this.promptErrorSource === "compaction" || turn.error?.codexErrorInfo === "other" && this.isCompacting());
1946
+ const compactionFailure = turn.status === "failed" && (this.terminalFailure.promptErrorSource === "compaction" || turn.error?.codexErrorInfo === "other" && this.isCompacting());
1745
1947
  this.settledTurnFailureFinalizationAllowed = turn.status === "failed" && (turn.error?.codexErrorInfo === "serverOverloaded" || compactionFailure);
1746
- if (turn.status !== "completed") this.responseCompletions.clear();
1948
+ if (turn.status !== "completed") this.usageProjection.invalidateContext();
1747
1949
  if (turn.status === "failed") {
1748
- this.promptError = resolveCodexPromptError({
1950
+ const codexErrorInfo = turn.error?.codexErrorInfo;
1951
+ this.terminalFailure.record({
1749
1952
  message: turn.error?.message,
1750
- codexErrorInfo: turn.error?.codexErrorInfo,
1751
- rateLimits: this.options.readRecentRateLimits?.()
1752
- }) ?? "codex app-server turn failed";
1753
- this.promptErrorSource = compactionFailure ? "compaction" : "prompt";
1953
+ codexErrorInfo,
1954
+ rateLimits: this.options.readRecentRateLimits?.(),
1955
+ fallbackMessage: "codex app-server turn failed",
1956
+ promptErrorSource: compactionFailure ? "compaction" : "prompt"
1957
+ });
1754
1958
  }
1755
1959
  if (compactionFailure) {
1756
1960
  const failedCompactionItemIds = [...this.activeCompactionItemIds];
@@ -1763,7 +1967,7 @@ var CodexAppServerEventProjector = class {
1763
1967
  const turnItems = turn.items ?? [];
1764
1968
  const unsettledAsyncDeliveries = this.asyncDeliveryProjection.pending();
1765
1969
  const lastToolItem = turnItems.findLast((item) => matchesCodexSnapshotTurn(item, this.turnId) && (item.type === "dynamicToolCall" || shouldClearTerminalPresentationForNativeItem(item)));
1766
- if (lastToolItem?.type !== "dynamicToolCall") this.clearTerminalPresentationForNativeItem(lastToolItem);
1970
+ if (lastToolItem?.type !== "dynamicToolCall") this.nativeToolLifecycleProjector.clearTerminalPresentationForNativeItem(lastToolItem);
1767
1971
  for (const item of turnItems) {
1768
1972
  if (!this.asyncDeliveryProjection.allows(item)) continue;
1769
1973
  this.diagnostics.warnUnknownItemStatus(item);
@@ -1771,15 +1975,21 @@ var CodexAppServerEventProjector = class {
1771
1975
  if (asyncMessage) await this.asyncDeliveryProjection.deliver(asyncMessage);
1772
1976
  if (this.projectionClosed) return;
1773
1977
  this.reasoningProjection.recordItem(item);
1774
- await this.generatedMediaProjection.recordNative(item);
1978
+ await this.settlement.project("media_projection", () => this.generatedMediaProjection.recordNative(item));
1775
1979
  if (this.projectionClosed) return;
1776
1980
  this.toolProgressProjection.recordToolMeta(item);
1777
1981
  this.toolProgressProjection.rememberCommandAggregateOutputEcho(item);
1778
- await this.emitSnapshotOnlyNativeToolProgress(item);
1982
+ await this.eventProjection.emitSnapshotOnlyNativeToolProgress({
1983
+ item,
1984
+ activeItemIds: this.activeItemIds,
1985
+ completedItemIds: this.completedItemIds,
1986
+ isActive: () => !this.projectionClosed
1987
+ });
1779
1988
  if (this.projectionClosed) return;
1780
1989
  this.toolTranscriptProjection.recordNativeToolCall(item);
1781
- await this.toolTranscriptProjection.recordNativeToolResultWithDetails(item);
1990
+ const details = await this.toolTranscriptProjection.prepareNativeToolResultDetails(item);
1782
1991
  if (this.projectionClosed) return;
1992
+ this.toolTranscriptProjection.recordNativeToolResult(item, details);
1783
1993
  this.toolTranscriptProjection.emitAfterToolCallObservation(item);
1784
1994
  this.toolProgressProjection.emitToolResultSummary(item);
1785
1995
  this.toolProgressProjection.emitToolResultOutput(item);
@@ -1794,52 +2004,12 @@ var CodexAppServerEventProjector = class {
1794
2004
  this.activeCompactionItemIds.clear();
1795
2005
  await this.reasoningProjection.maybeEndReasoning();
1796
2006
  }
1797
- async emitSnapshotOnlyNativeToolProgress(item) {
1798
- if (!shouldSynthesizeToolProgressForItem(item) || !matchesCodexSnapshotTurn(item, this.turnId) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
1799
- if (!this.activeItemIds.has(item.id)) {
1800
- this.eventProjection.emitStandardItemEvent({
1801
- phase: "start",
1802
- item
1803
- });
1804
- await this.eventProjection.emitNormalizedToolItemEvent({
1805
- phase: "start",
1806
- item
1807
- });
1808
- }
1809
- if (this.projectionClosed) return;
1810
- this.activeItemIds.delete(item.id);
1811
- this.eventProjection.emitStandardItemEvent({
1812
- phase: "end",
1813
- item
1814
- });
1815
- await this.eventProjection.emitNormalizedToolItemEvent({
1816
- phase: "result",
1817
- item
1818
- });
1819
- this.completedItemIds.add(item.id);
1820
- }
1821
2007
  async handleRawResponseItemCompleted(params) {
1822
2008
  const item = isJsonObject(params.item) ? params.item : void 0;
1823
2009
  if (!item) return;
1824
2010
  this.toolTranscriptProjection.recordRawNativeToolItem(item);
1825
2011
  this.assistantProjection.handleRawResponseItemCompleted(item, this.activeItemIds);
1826
- await this.generatedMediaProjection.recordRaw(item);
1827
- }
1828
- clearTerminalPresentationForNativeItem(item) {
1829
- if (!item || this.terminalPresentationClearedItemIds.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
1830
- const toolCallOrdinal = this.nativeToolOutcomeOrdinals.get(item.id);
1831
- this.terminalPresentationClearedItemIds.add(item.id);
1832
- this.params.onToolOutcome?.({
1833
- toolName: itemName(item) ?? item.type,
1834
- argsHash: "",
1835
- resultHash: "",
1836
- ...toolCallOrdinal !== void 0 ? { toolCallOrdinal } : {},
1837
- terminalPresentation: void 0,
1838
- presentationOnly: true
1839
- });
1840
- }
1841
- emitAgentEvent(event) {
1842
- if (!this.projectionClosed) emitCodexAgentEvent(this.params, event);
2012
+ await this.settlement.project("media_projection", () => this.generatedMediaProjection.recordRaw(item));
1843
2013
  }
1844
2014
  isHookNotificationForCurrentThread(params) {
1845
2015
  const threadId = readStringField(params, "threadId");
@@ -1950,6 +2120,7 @@ function createNativeMcpRuntime(params) {
1950
2120
  listResourceTemplates: async (serverName) => {
1951
2121
  return { resourceTemplates: (await loadStatuses()).find((entry) => entry.name === serverName)?.resourceTemplates ?? [] };
1952
2122
  },
2123
+ joinCleanup: async () => {},
1953
2124
  dispose: async () => {}
1954
2125
  };
1955
2126
  return runtime;
@@ -2107,7 +2278,6 @@ const MAX_USER_INPUT_HEADER = 64;
2107
2278
  const MAX_USER_INPUT_TEXT = 4096;
2108
2279
  /** Creates the single per-turn owner for Codex operator input. */
2109
2280
  function createCodexUserInputBridge(params) {
2110
- const gatewayCall = params.gatewayCall ?? callGatewayTool;
2111
2281
  const jobs = [];
2112
2282
  let activeCompletion;
2113
2283
  const inputSessionKey = params.paramsForRun.sessionKey ?? params.paramsForRun.sessionId;
@@ -2158,7 +2328,7 @@ function createCodexUserInputBridge(params) {
2158
2328
  agentId: params.paramsForRun.agentId,
2159
2329
  runId: params.paramsForRun.runId,
2160
2330
  timeoutMs,
2161
- gatewayCall,
2331
+ gatewayCall: params.gatewayCall,
2162
2332
  delivery: params.paramsForRun,
2163
2333
  signal,
2164
2334
  promptOptions: {
@@ -2188,9 +2358,9 @@ function createCodexUserInputBridge(params) {
2188
2358
  });
2189
2359
  },
2190
2360
  async handleElicitationRequest(request) {
2191
- if (readRawOwnString(request.params, "threadId") !== params.threadId) return;
2361
+ if (readOwnDataString(request.params, "threadId") !== params.threadId) return;
2192
2362
  const requestSnapshot = agentHarnessStructuredInput.snapshot(request.params);
2193
- if (requestSnapshot === void 0) {
2363
+ if (!agentHarnessStructuredInput.isRecord(requestSnapshot)) {
2194
2364
  const cancelValue = createCodexElicitationResponse("cancel");
2195
2365
  return await enqueue({
2196
2366
  requestId: request.id,
@@ -2206,10 +2376,10 @@ function createCodexUserInputBridge(params) {
2206
2376
  }
2207
2377
  });
2208
2378
  }
2209
- const { compileCodexOrdinaryElicitation } = await import("./elicitation-input-DMkeki8d.js");
2379
+ if (readOwnDataString(requestSnapshot, "threadId") !== params.threadId) return;
2380
+ const { compileCodexOrdinaryElicitation } = await import("./elicitation-input-DPoK8F7w.js");
2210
2381
  const compiled = compileCodexOrdinaryElicitation({
2211
- value: requestSnapshot,
2212
- threadId: params.threadId,
2382
+ snapshot: requestSnapshot,
2213
2383
  turnId: params.turnId
2214
2384
  });
2215
2385
  if (compiled.kind === "ignored") return;
@@ -2235,7 +2405,7 @@ function createCodexUserInputBridge(params) {
2235
2405
  handleNotification(notification) {
2236
2406
  if (notification.method !== "serverRequest/resolved" || !isJsonObject(notification.params)) return;
2237
2407
  const requestId = readRequestId(notification.params);
2238
- if (requestId === void 0 || readOwnString(notification.params, "threadId") !== params.threadId) return;
2408
+ if (requestId === void 0 || readOwnDataString(notification.params, "threadId") !== params.threadId) return;
2239
2409
  const job = jobs.find((candidate) => typeof candidate.requestId === typeof requestId && candidate.requestId === requestId);
2240
2410
  if (job) cancelJob(job);
2241
2411
  },
@@ -2314,11 +2484,7 @@ function readArray(record, key, maximum) {
2314
2484
  const value = readValue(record, key);
2315
2485
  return Array.isArray(value) && value.length <= maximum ? value : void 0;
2316
2486
  }
2317
- function readOwnString(record, key) {
2318
- const descriptor = Object.getOwnPropertyDescriptor(record, key);
2319
- return descriptor && "value" in descriptor && typeof descriptor.value === "string" ? descriptor.value : void 0;
2320
- }
2321
- function readRawOwnString(value, key) {
2487
+ function readOwnDataString(value, key) {
2322
2488
  if (!value || typeof value !== "object" || Array.isArray(value)) return;
2323
2489
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
2324
2490
  return descriptor && "value" in descriptor && typeof descriptor.value === "string" ? descriptor.value : void 0;
@@ -2350,6 +2516,22 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2350
2516
  const { emitExecutionPhaseOnce, emitLifecycleStart, maybeAnnounceFastModeAutoOff } = lifecycle;
2351
2517
  const { enqueueNotification } = notifications;
2352
2518
  const activeTurnId = turn.turn.id;
2519
+ const { thread } = resourceState;
2520
+ const runtimeModelSelection = thread.preserveNativeModel && thread.model && thread.modelProvider ? {
2521
+ provider: thread.modelProvider,
2522
+ model: thread.model
2523
+ } : void 0;
2524
+ const projectionParams = runtimeModelSelection ? {
2525
+ ...dynamicToolParams,
2526
+ provider: runtimeModelSelection.provider,
2527
+ modelId: runtimeModelSelection.model,
2528
+ model: {
2529
+ ...dynamicToolParams.model,
2530
+ id: runtimeModelSelection.model,
2531
+ name: runtimeModelSelection.model,
2532
+ provider: runtimeModelSelection.provider
2533
+ }
2534
+ } : dynamicToolParams;
2353
2535
  const progressCardTool = toolBridge.availableTools.find((tool) => tool.name === "progress_card");
2354
2536
  let nativePlanUpdateOrdinal = 0;
2355
2537
  const prepareNativeMcpAppResultDetails = createCodexNativeMcpAppResultDetailsPreparer({
@@ -2375,7 +2557,7 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2375
2557
  imagesCount: params.images?.length ?? 0
2376
2558
  });
2377
2559
  projectorRef.current = new CodexAppServerEventProjector({
2378
- ...dynamicToolParams,
2560
+ ...projectionParams,
2379
2561
  onAgentEvent: (event) => {
2380
2562
  if (event.stream === "assistant" && typeof event.data.delta === "string") {
2381
2563
  streamState.eventEmitted = true;
@@ -2390,7 +2572,7 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2390
2572
  asyncUserMessageAllowed: params.disableTools !== true && (params.toolsAllow === void 0 || toolBridge.availableTools.some((tool) => tool.name === "message")),
2391
2573
  onAsyncDelivery: async (delivery) => {
2392
2574
  return await codexTranscriptMirrorRuntime.deliverAsyncMessageBestEffort({
2393
- params: dynamicToolParams,
2575
+ params: projectionParams,
2394
2576
  cwd: effectiveCwd,
2395
2577
  threadId: resourceState.thread.threadId,
2396
2578
  turnId: activeTurnId,
@@ -2447,6 +2629,7 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2447
2629
  }
2448
2630
  });
2449
2631
  if (isTerminalTurnStatus(turn.turn.status)) {
2632
+ projectorRef.current.settlement.terminalReceipt = turn.turn;
2450
2633
  state.terminalTurnNotificationQueued = true;
2451
2634
  deadlines.beginSettlement(Date.now());
2452
2635
  }
@@ -2510,7 +2693,7 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2510
2693
  }
2511
2694
  };
2512
2695
  const assertSteeringActive = () => {
2513
- params.hostCapabilities.assertActive();
2696
+ connection.assertCurrent();
2514
2697
  runAbortController.signal.throwIfAborted();
2515
2698
  if (state.completed || state.terminalTurnNotificationQueued) throw new Error("codex app-server turn is no longer accepting steering");
2516
2699
  };
@@ -2548,13 +2731,15 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2548
2731
  if (result.failedMediaCount) throw new Error(`failed to hydrate ${result.failedMediaCount} structured image attachment(s) for Codex steering`);
2549
2732
  return buildCodexUserInput(text, result.images);
2550
2733
  },
2551
- beforeConfirmConsumed: async (items) => {
2734
+ beforeSubmit: async (items) => {
2552
2735
  const transcriptItems = items.filter((item) => item.isInboundUserMessage === true || item.userTurnTranscriptRecorder !== void 0);
2553
2736
  if (transcriptItems.length === 0) return;
2554
2737
  await promptMirrorPromise;
2738
+ assertSteeringActive();
2555
2739
  const messages = activeProjector.buildSteeringTranscriptPrefix();
2556
2740
  if (params.sessionTarget && messages.length > 0) {
2557
2741
  await codexTranscriptMirrorRuntime.mirror({
2742
+ assertCurrent: assertSteeringActive,
2558
2743
  agentId: sessionAgentId,
2559
2744
  sessionKey: contextSessionKey,
2560
2745
  sessionId: params.sessionId,
@@ -2566,47 +2751,66 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2566
2751
  runMirrorIdentityPrefix: `${activeTurnId}:`,
2567
2752
  config: params.config
2568
2753
  });
2754
+ assertSteeringActive();
2569
2755
  activeProjector.markSteeringTranscriptPersisted();
2570
2756
  }
2571
2757
  for (const item of transcriptItems) {
2572
2758
  const recorder = item.userTurnTranscriptRecorder;
2573
2759
  if (!recorder) continue;
2760
+ assertSteeringActive();
2574
2761
  await recorder.persistApproved();
2575
- if (!recorder.hasPersisted()) throw new Error("Codex consumed steering before its user turn was persisted");
2762
+ if (!recorder.hasPersisted()) throw new Error("Codex steering requires a persisted user turn before submission");
2576
2763
  }
2577
2764
  }
2578
2765
  });
2579
2766
  steeringQueueRef.current = activeSteeringQueue;
2580
- const claimPendingUserInputAnswer = async (text, optionsLocal) => {
2767
+ const injectionGuard = (assertCurrent) => () => {
2768
+ assertCurrent?.();
2769
+ assertSteeringActive();
2770
+ return true;
2771
+ };
2772
+ const claimPendingUserInputAnswer = async (text, optionsLocal, assertCurrent, authorityKind = assertCurrent ? "source-bound" : "run") => {
2581
2773
  if (optionsLocal?.isInboundUserMessage !== true || hasPromptImageInput(optionsLocal)) return false;
2582
2774
  assertSteeringActive();
2583
2775
  return await claimPendingAgentQuestionAnswer({
2584
2776
  sessionKey: params.sessionKey ?? params.sessionId,
2585
2777
  text,
2778
+ authority: {
2779
+ kind: authorityKind,
2780
+ assertCurrent: injectionGuard(assertCurrent)
2781
+ },
2782
+ sourceRecorder: optionsLocal.userTurnTranscriptRecorder,
2586
2783
  persist: optionsLocal.userTurnTranscriptRecorder ? async () => {
2587
2784
  await optionsLocal.userTurnTranscriptRecorder?.persistApproved();
2588
2785
  } : void 0
2589
2786
  });
2590
2787
  };
2591
- const cancelPendingUserInput = (resolvedBy) => cancelPendingAgentQuestionForSession({
2788
+ const cancelPendingUserInput = (resolvedBy, assertCurrent, authorityKind = assertCurrent ? "source-bound" : "run") => cancelPendingAgentQuestionForSession({
2592
2789
  sessionKey: params.sessionKey ?? params.sessionId,
2593
- resolvedBy
2790
+ resolvedBy,
2791
+ authority: {
2792
+ kind: authorityKind,
2793
+ assertCurrent: injectionGuard(assertCurrent)
2794
+ }
2594
2795
  });
2595
- const queueMessage = async (text, optionsLocal) => {
2596
- if (await claimPendingUserInputAnswer(text, optionsLocal)) {
2796
+ const queueMessage = async (text, optionsLocal, assertCurrent, authorityKind = assertCurrent ? "source-bound" : "run") => {
2797
+ const canClaim = injectionGuard(assertCurrent);
2798
+ if (await claimPendingUserInputAnswer(text, optionsLocal, assertCurrent, authorityKind)) {
2597
2799
  optionsLocal?.onQueueAccepted?.(true);
2598
2800
  return;
2599
2801
  }
2600
2802
  if (optionsLocal?.isInboundUserMessage === true && hasPromptImageInput(optionsLocal)) {
2601
2803
  assertSteeringActive();
2602
2804
  try {
2603
- await cancelPendingUserInput("image-reply");
2805
+ await cancelPendingUserInput("image-reply", assertCurrent, authorityKind);
2604
2806
  } catch (error) {
2807
+ canClaim();
2808
+ if (error instanceof Error && error.name === "QuestionDispatchRefusedError") throw error;
2605
2809
  embeddedAgentLog.warn("failed to cancel codex gateway question before image steering", { error });
2606
2810
  }
2607
2811
  }
2608
2812
  try {
2609
- await activeSteeringQueue.queue(text, optionsLocal);
2813
+ await activeSteeringQueue.queue(text, optionsLocal, injectionGuard(assertCurrent));
2610
2814
  } catch (error) {
2611
2815
  if (error instanceof CodexSteeringAcceptedUnconfirmedError) return {
2612
2816
  transcriptCommit: "unconfirmed",
@@ -2615,6 +2819,13 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2615
2819
  throw error;
2616
2820
  }
2617
2821
  };
2822
+ const messageInjection = {
2823
+ version: 2,
2824
+ isAvailable: () => !state.completed && !state.terminalTurnNotificationQueued && !runAbortController.signal.aborted,
2825
+ queueMessage,
2826
+ claimPendingUserInputAnswer,
2827
+ cancelPendingUserInput
2828
+ };
2618
2829
  const handle = {
2619
2830
  kind: "embedded",
2620
2831
  runId: params.runId,
@@ -2632,10 +2843,8 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2632
2843
  claimPendingUserInputAnswer,
2633
2844
  cancelPendingUserInput,
2634
2845
  queueMessage,
2635
- messageInjection: {
2636
- isAvailable: () => !state.completed && !state.terminalTurnNotificationQueued && !runAbortController.signal.aborted,
2637
- queueMessage
2638
- },
2846
+ messageInjection,
2847
+ messageInjectionV2: messageInjection,
2639
2848
  isStreaming: () => !state.completed && !runAbortController.signal.aborted,
2640
2849
  isAborted: () => runAbortController.signal.aborted,
2641
2850
  isStopped: () => state.completed || runAbortController.signal.aborted,
@@ -2650,7 +2859,7 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2650
2859
  abort: () => abortExplicitly("aborted")
2651
2860
  };
2652
2861
  params.replyOperation?.attachBackend(handle);
2653
- setActiveEmbeddedRun(params.sessionId, handle, params.sessionKey, params.sessionFile);
2862
+ setActiveEmbeddedRun(params.sessionId, handle, params.sessionKey, params.sessionFile, sessionAgentId);
2654
2863
  const freezeRunTerminalOutcome = () => {
2655
2864
  if (terminalState.terminalOutcomeFrozen) return;
2656
2865
  terminalState.terminalOutcomeFrozen = true;
@@ -2663,6 +2872,7 @@ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notificatio
2663
2872
  return {
2664
2873
  activeTurnId,
2665
2874
  activeProjector,
2875
+ runtimeModelSelection,
2666
2876
  streamState,
2667
2877
  handle,
2668
2878
  freezeRunTerminalOutcome,
@@ -2686,7 +2896,9 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2686
2896
  const projectionClose = state.projectionClosed ? void 0 : activeTurn.activeProjector.closeProjection();
2687
2897
  state.projectionClosed = true;
2688
2898
  const checkpointCleanup = projectionClose ? runCleanupStep("codex-transcript-checkpoint", () => projectionClose) : void 0;
2689
- await state.abortCleanup.catch(() => void 0);
2899
+ if (params.oneShotCliRun) await runCleanupStep("codex-abort-cleanup", () => state.abortCleanup);
2900
+ else await state.abortCleanup?.catch(() => {});
2901
+ if (params.oneShotCliRun) await runCleanupStep("codex-one-shot-terminals", () => terminateCodexBackgroundTerminals(resourceState.client, resourceState.thread.threadId, true));
2690
2902
  try {
2691
2903
  steeringQueueRef.current?.cancel();
2692
2904
  if (params.isFinalFallbackAttempt !== false) await maybeEmitFastModeAutoResetBestEffort();
@@ -2707,9 +2919,9 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2707
2919
  aborted: runAbortController.signal.aborted && !state.clientClosedAbort
2708
2920
  });
2709
2921
  await runCleanupStep("codex-trajectory-flush", () => trajectoryRecorder?.flush());
2710
- const retainLiveIncognitoThread = (terminalState.turnSucceeded || state.permissionChangeRestart === "confirmed" && !params.abortSignal?.aborted) && isIncognitoSessionKey(params.sessionKey);
2711
- const retainLiveThread = ((retainLiveIncognitoThread && resourceState.thread.liveThreadEphemeralPolicy !== void 0 || terminalState.turnSucceeded && !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) ? (await bindingStore.read(bindingIdentity))?.threadId === resourceState.thread.threadId && await bindingStore.withLease(bindingIdentity, async () => {
2712
- if ((await bindingStore.read(bindingIdentity))?.threadId !== resourceState.thread.threadId) return false;
2922
+ const retainLiveIncognitoThread = (terminalState.settledTurnStatus === "completed" || state.permissionChangeRestart === "confirmed" && !params.abortSignal?.aborted) && isIncognitoSessionKey(params.sessionKey);
2923
+ 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 () => {
2924
+ if (bindingStore.read(bindingIdentity)?.threadId !== resourceState.thread.threadId) return false;
2713
2925
  return await retainCodexAppServerBindingSubscription(resourceState.client, resourceState.thread.threadId, {
2714
2926
  release: resourceState.thread.liveThreadOwnership?.release,
2715
2927
  configFingerprint: resourceState.thread.liveThreadConfigFingerprint,
@@ -2726,16 +2938,22 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2726
2938
  if (!await unsubscribeCodexThreadBestEffort(resourceState.client, {
2727
2939
  threadId: resourceState.thread.threadId,
2728
2940
  timeoutMs: 5e3
2729
- })) await closeCodexStartupClientBestEffort(resourceState.client);
2941
+ })) {
2942
+ await closeCodexStartupClientBestEffort(resourceState.client);
2943
+ if (params.oneShotCliRun) await runCleanupStep("codex-one-shot-unsubscribe", async () => {
2944
+ throw new Error("Codex one-shot thread unsubscribe was not confirmed");
2945
+ });
2946
+ }
2730
2947
  }
2731
2948
  }
2732
2949
  } finally {
2733
2950
  await runCleanupStep("codex-user-input-cancel", () => userInputBridgeRef.current?.cancelPending());
2734
2951
  await runCleanupStep("codex-turn-deadline-clear", () => deadlines.dispose());
2735
2952
  await runCleanupStep("codex-dynamic-tool-cleanup", async () => {
2736
- const cleanupReason = terminalState.turnSucceeded ? "completion" : state.timeout ? "timeout" : runAbortController.signal.aborted ? "cancel" : "error";
2953
+ const cleanupReason = terminalState.settledTurnStatus === "completed" ? "completion" : state.timeout ? "timeout" : runAbortController.signal.aborted ? "cancel" : "error";
2737
2954
  const cleanups = prompt.context.attemptTools.runCleanups.splice(0);
2738
- await Promise.allSettled(cleanups.map(async (cleanup) => await cleanup(cleanupReason)));
2955
+ const errors = (await Promise.allSettled(cleanups.map(async (cleanup) => await cleanup(cleanupReason)))).filter((result) => result.status === "rejected");
2956
+ if (params.oneShotCliRun && errors.length) throw new AggregateError(errors.map((result) => result.reason), "Codex tool cleanup failed");
2739
2957
  });
2740
2958
  await runCleanupStep("codex-route-release", releaseCurrentRoute);
2741
2959
  await checkpointCleanup;
@@ -2744,15 +2962,13 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2744
2962
  resourceState.nativeHookRelay = void 0;
2745
2963
  await runCleanupStep("codex-native-hook-relay-release", () => {
2746
2964
  if (!nativeHookRelay) return;
2747
- if (state.shouldDelayNativeHookRelayUnregister) scheduleCodexNativeHookRelayUnregister({
2965
+ if (state.shouldDelayNativeHookRelayUnregister && !params.oneShotCliRun) scheduleCodexNativeHookRelayUnregister({
2748
2966
  relay: nativeHookRelay,
2749
2967
  hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec
2750
2968
  });
2751
2969
  else nativeHookRelay.unregister();
2752
2970
  });
2753
2971
  await runCleanupStep("codex-sandbox-release", releaseSandboxExecEnvironment);
2754
- await runCleanupStep("codex-scoped-mcp-dispose", () => prompt.context.attemptTools.scopedMcpTools?.dispose());
2755
- await runCleanupStep("codex-configured-mcp-dispose", () => prompt.context.attemptTools.configuredMcp?.dispose());
2756
2972
  await runCleanupStep("codex-abort-listener-remove", () => {
2757
2973
  runAbortController.signal.removeEventListener("abort", abortListener);
2758
2974
  });
@@ -2995,8 +3211,9 @@ async function readCodexAppServerRolloutTokenSnapshot(file, openedHandle) {
2995
3211
  bytesRead += result.bytesRead;
2996
3212
  }
2997
3213
  let lineEnd = bytesRead;
2998
- for (let index = bytesRead - 1; index >= 0; index -= 1) {
2999
- if (chunk[index] !== 10) continue;
3214
+ while (lineEnd > 0) {
3215
+ const index = chunk.lastIndexOf(10, lineEnd - 1);
3216
+ if (index < 0) break;
3000
3217
  const lineFragment = chunk.subarray(index + 1, lineEnd);
3001
3218
  const line = partialLineFragments.length === 0 ? lineFragment.toString("utf8") : Buffer.concat([lineFragment, ...partialLineFragments.toReversed()]).toString("utf8");
3002
3219
  partialLineFragments.length = 0;
@@ -3078,6 +3295,7 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
3078
3295
  await Promise.all(rolloutFiles.map(async (file) => {
3079
3296
  await file.handle?.close();
3080
3297
  }));
3298
+ assertCodexBindingMayBeReplaced(binding, "rotating an oversized native transcript", params.expectedSessionRuntimeOwnership);
3081
3299
  embeddedAgentLog.warn("codex app-server native transcript exceeded active byte limit; starting a fresh thread", {
3082
3300
  threadId: binding.threadId,
3083
3301
  maxBytes,
@@ -3089,7 +3307,7 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
3089
3307
  await params.bindingStore.mutate(params.identity, {
3090
3308
  kind: "clear",
3091
3309
  threadId: binding.threadId
3092
- });
3310
+ }, params.assertCurrent);
3093
3311
  return { binding: void 0 };
3094
3312
  }
3095
3313
  }
@@ -3106,6 +3324,7 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
3106
3324
  const sessionTokens = sessionRecord?.totalTokensFresh === true && sessionRecord.totalTokensVersion === 1 && typeof sessionRecord?.totalTokens === "number" && Number.isFinite(sessionRecord.totalTokens) ? sessionRecord.totalTokens : void 0;
3107
3325
  const tokenCount = maxFiniteNumber([sessionTokens, nativeTokens]);
3108
3326
  if (tokenCount !== void 0 && tokenCount >= maxTokens) {
3327
+ assertCodexBindingMayBeReplaced(binding, "rotating a full native context", params.expectedSessionRuntimeOwnership);
3109
3328
  embeddedAgentLog.warn("codex app-server native transcript exceeded active token limit; starting a fresh thread", {
3110
3329
  threadId: binding.threadId,
3111
3330
  maxTokens,
@@ -3120,7 +3339,7 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
3120
3339
  await params.bindingStore.mutate(params.identity, {
3121
3340
  kind: "clear",
3122
3341
  threadId: binding.threadId
3123
- });
3342
+ }, params.assertCurrent);
3124
3343
  return { binding: void 0 };
3125
3344
  }
3126
3345
  const startupContextTokens = nativeModelContextWindow ?? sessionModelContextWindow;
@@ -3142,7 +3361,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3142
3361
  offAnnounced: false,
3143
3362
  resetAnnounced: false
3144
3363
  };
3145
- const preDynamicStartupStages = createCodexDynamicToolBuildStageTracker({ enabled: profilerEnabled });
3364
+ const preDynamicStartupStages = createCodexDynamicToolBuildStageTracker();
3146
3365
  const runtimeArtifactRequest = params.captureRuntimeArtifact || params.expectedRuntimeArtifact ? params.expectedRuntimeArtifact ? { expected: params.expectedRuntimeArtifact } : {} : void 0;
3147
3366
  const pluginConfig = readCodexPluginConfig(options.pluginConfig);
3148
3367
  const requirementsToml = readCodexRequirementsToml({});
@@ -3178,7 +3397,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3178
3397
  const preparedEnvironment = params.hostCapabilities.preparedEnvironment?.();
3179
3398
  const remoteExec = isCodexRemoteExecPlacementSandbox(sandbox);
3180
3399
  const assertLocalTargetSupported = (unsupported) => {
3181
- if (preparedEnvironment?.localProcessEnv && unsupported) throw new Error("This runtime cannot target the diagnosed local installation. Use the saved prompt with a suggested external or manual handoff on this machine.");
3400
+ if (preparedEnvironment?.localProcessEnv && unsupported) throw new Error("This runtime cannot target the diagnosed local installation. Use an owned local Codex stdio process, or use the saved prompt with a suggested external or manual handoff on this machine.");
3182
3401
  };
3183
3402
  assertLocalTargetSupported(sandbox?.enabled === true || remoteExec);
3184
3403
  const preparedShellEnvironment = preparedEnvironment ? {
@@ -3189,7 +3408,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3189
3408
  const shellEnvironment = preparedShellEnvironment && Object.keys(preparedShellEnvironment).length > 0 ? preparedShellEnvironment : void 0;
3190
3409
  const disableLoginShell = remoteExec || preparedEnvironment?.localProcessEnv !== void 0 || preparedEnvironment?.managedLocalIdentity === true || preparedEnvironment !== void 0 && Object.keys(preparedEnvironment.credentialScrubEnv).length > 0;
3191
3410
  const withPreparedProcessEnv = (appServer) => {
3192
- assertLocalTargetSupported(appServer.start.transport === "websocket" || Boolean(appServer.remoteWorkspaceRoot));
3411
+ assertLocalTargetSupported(appServer.start.transport !== "stdio" || Boolean(appServer.remoteWorkspaceRoot));
3193
3412
  return shellEnvironment ? {
3194
3413
  ...appServer,
3195
3414
  start: {
@@ -3233,16 +3452,17 @@ async function prepareCodexAttemptConnection({ params, options }) {
3233
3452
  bindingIdentity = physicalIdentity;
3234
3453
  }
3235
3454
  }
3236
- let startupBinding = await bindingStore.read(bindingIdentity);
3237
- if (!startupBinding && bindingIdentity.kind === "session" && bindingIdentity.sessionKey) {
3238
- if (!await reclaimCurrentCodexSessionGeneration({
3239
- bindingStore,
3240
- identity: bindingIdentity,
3241
- config: params.config,
3242
- storePath: params.sessionTarget?.storePath
3243
- })) throw createCodexSessionGenerationSupersededError(bindingIdentity.sessionId);
3244
- startupBinding = await bindingStore.read(bindingIdentity);
3245
- }
3455
+ const { binding: admittedBinding, assertCurrent } = await resolveCodexSessionBinding({
3456
+ reclaimStale: true,
3457
+ bindingStore,
3458
+ identity: bindingIdentity,
3459
+ config: params.config,
3460
+ storePath: params.sessionTarget?.storePath,
3461
+ assertCurrent: params.hostCapabilities.assertActive,
3462
+ signal: params.abortSignal,
3463
+ assertBinding: params.expectedSessionRuntimeOwnership ? (binding) => assertCodexSessionRuntimeOwnership(binding, params.expectedSessionRuntimeOwnership) : void 0
3464
+ });
3465
+ let startupBinding = admittedBinding;
3246
3466
  preDynamicStartupStages.mark("read-binding");
3247
3467
  const usesSupervisionConnection = startupBinding?.connectionScope === "supervision";
3248
3468
  if (usesSupervisionConnection) activeContextEngine = void 0;
@@ -3365,7 +3585,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3365
3585
  preDynamicStartupStages.mark("app-server-policy");
3366
3586
  preDynamicStartupStages.mark("native-hook-relay");
3367
3587
  const terminalState = {
3368
- turnSucceeded: false,
3588
+ settledTurnStatus: void 0,
3369
3589
  explicitCancellationObserved: false,
3370
3590
  explicitCancellationReason: void 0,
3371
3591
  terminalOutcomeFrozen: false,
@@ -3393,104 +3613,113 @@ async function prepareCodexAttemptConnection({ params, options }) {
3393
3613
  };
3394
3614
  if (params.abortSignal?.aborted) abortFromUpstream();
3395
3615
  else params.abortSignal?.addEventListener("abort", abortFromUpstream, { once: true });
3396
- const startupBindingBeforeRotation = startupBinding;
3397
- const startupBindingResolution = await rotateOversizedCodexAppServerStartupBinding({
3398
- binding: startupBinding,
3399
- bindingStore,
3400
- identity: bindingIdentity,
3401
- sessionFile: params.sessionFile,
3402
- agentDir,
3403
- codexHome: appServer.start.env?.CODEX_HOME,
3404
- config: params.config,
3405
- contextEngineActive: Boolean(activeContextEngine)
3406
- });
3407
- startupBinding = startupBindingResolution.binding;
3408
- const initialInactiveThreadBootstrapBindingForcedFreshStart = initialStartupBindingHadInactiveThreadBootstrap && !startupBinding?.threadId;
3409
- preDynamicStartupStages.mark("rotate-binding");
3410
- if (startupBinding !== startupBindingBeforeRotation) {
3411
- reviewerPolicyContext = resolveReviewerPolicyContext(startupBinding);
3412
- configuredAppServer = resolveRuntimeOptionsForBinding(startupBinding, {
3413
- modelProvider: reviewerPolicyContext.modelProvider,
3414
- model: reviewerPolicyContext.model
3616
+ try {
3617
+ const startupBindingBeforeRotation = startupBinding;
3618
+ const startupBindingResolution = await rotateOversizedCodexAppServerStartupBinding({
3619
+ assertCurrent,
3620
+ binding: startupBinding,
3621
+ bindingStore,
3622
+ identity: bindingIdentity,
3623
+ sessionFile: params.sessionFile,
3624
+ agentDir,
3625
+ codexHome: appServer.start.env?.CODEX_HOME,
3626
+ config: params.config,
3627
+ contextEngineActive: Boolean(activeContextEngine),
3628
+ expectedSessionRuntimeOwnership: params.expectedSessionRuntimeOwnership
3415
3629
  });
3416
- resolvedAppServer = resolveFinalAppServer(configuredAppServer, reviewerPolicyContext);
3417
- appServer = resolvedAppServer.appServer;
3630
+ startupBinding = startupBindingResolution.binding;
3631
+ const initialInactiveThreadBootstrapBindingForcedFreshStart = initialStartupBindingHadInactiveThreadBootstrap && !startupBinding?.threadId;
3632
+ preDynamicStartupStages.mark("rotate-binding");
3633
+ if (startupBinding !== startupBindingBeforeRotation) {
3634
+ reviewerPolicyContext = resolveReviewerPolicyContext(startupBinding);
3635
+ configuredAppServer = resolveRuntimeOptionsForBinding(startupBinding, {
3636
+ modelProvider: reviewerPolicyContext.modelProvider,
3637
+ model: reviewerPolicyContext.model
3638
+ });
3639
+ resolvedAppServer = resolveFinalAppServer(configuredAppServer, reviewerPolicyContext);
3640
+ appServer = resolvedAppServer.appServer;
3641
+ }
3642
+ const sessionPermissionPolicy = resolveCodexEffectiveSessionPermissionPolicy({
3643
+ appServer,
3644
+ permissionMode: params.permissionMode,
3645
+ sessionRoot: params.sessionRoot,
3646
+ defaultRoot: effectiveWorkspace
3647
+ });
3648
+ if (sessionPermissionPolicy) {
3649
+ params.permissionMode = sessionPermissionPolicy.mode;
3650
+ params.sessionRoot = sessionPermissionPolicy.root;
3651
+ (params.execOverrides ??= {}).mode = sessionPermissionPolicy.execMode;
3652
+ }
3653
+ const nativeHookRelayEvents = resolveCodexNativeHookRelayEvents({
3654
+ configuredEvents: options.nativeHookRelay?.events,
3655
+ appServer
3656
+ });
3657
+ const mutable = {
3658
+ startupBinding,
3659
+ startupContextTokens: startupBindingResolution.startupContextTokens,
3660
+ pluginAppServer: appServer,
3661
+ continuityCalibration: startupBindingBeforeRotation?.continuityCalibration
3662
+ };
3663
+ const resolveRuntimeOptionsForCurrentBinding = (selection) => resolveFinalAppServer(resolveRuntimeOptionsForBinding(mutable.startupBinding, selection), selection).appServer;
3664
+ assertCurrent();
3665
+ return {
3666
+ params,
3667
+ assertCurrent,
3668
+ options,
3669
+ attemptStartedAt,
3670
+ profilerEnabled,
3671
+ codexModelCallTrace,
3672
+ codexModelContentCapture,
3673
+ codexModelCallId,
3674
+ fastModeAutoStartedAtMs,
3675
+ fastModeAutoProgressState,
3676
+ preDynamicStartupStages,
3677
+ attemptClientFactory,
3678
+ runtimeArtifactRequest,
3679
+ pluginConfig,
3680
+ computerUseConfig,
3681
+ sessionAgentId,
3682
+ policyAgentId,
3683
+ resolvedWorkspace,
3684
+ sandboxSessionKey,
3685
+ contextSessionKey,
3686
+ sandbox,
3687
+ agentDir,
3688
+ shellEnvironment,
3689
+ disableLoginShell,
3690
+ bindingIdentity,
3691
+ bindingStore,
3692
+ activeContextEngine,
3693
+ isInactiveThreadBootstrapBinding,
3694
+ usesSupervisionConnection,
3695
+ startupAuthProfileId,
3696
+ startupAuthRequirement: preparedAuthRoute?.authRequirement,
3697
+ startupPreparedAuth,
3698
+ startupClientAuthProfileId,
3699
+ effectiveWorkspace,
3700
+ effectiveCwd,
3701
+ appServer,
3702
+ sessionPermissionPolicy,
3703
+ nativeHookRelayEvents,
3704
+ runAbortController,
3705
+ terminalState,
3706
+ abortExplicitly,
3707
+ abortFromUpstream,
3708
+ resolveReviewerPolicyContext,
3709
+ resolveRuntimeOptionsForCurrentBinding,
3710
+ mutable,
3711
+ initialStartupBindingHadInactiveThreadBootstrap,
3712
+ initialInactiveThreadBootstrapBindingForcedFreshStart
3713
+ };
3714
+ } catch (error) {
3715
+ params.abortSignal?.removeEventListener("abort", abortFromUpstream);
3716
+ throw error;
3418
3717
  }
3419
- const sessionPermissionPolicy = resolveCodexEffectiveSessionPermissionPolicy({
3420
- appServer,
3421
- permissionMode: params.permissionMode,
3422
- sessionRoot: params.sessionRoot,
3423
- defaultRoot: effectiveWorkspace
3424
- });
3425
- if (sessionPermissionPolicy) {
3426
- params.permissionMode = sessionPermissionPolicy.mode;
3427
- params.sessionRoot = sessionPermissionPolicy.root;
3428
- (params.execOverrides ??= {}).mode = sessionPermissionPolicy.execMode;
3429
- }
3430
- const nativeHookRelayEvents = resolveCodexNativeHookRelayEvents({
3431
- configuredEvents: options.nativeHookRelay?.events,
3432
- appServer
3433
- });
3434
- const mutable = {
3435
- startupBinding,
3436
- startupContextTokens: startupBindingResolution.startupContextTokens,
3437
- pluginAppServer: appServer,
3438
- continuityCalibration: startupBindingBeforeRotation?.continuityCalibration
3439
- };
3440
- const resolveRuntimeOptionsForCurrentBinding = (selection) => resolveFinalAppServer(resolveRuntimeOptionsForBinding(mutable.startupBinding, selection), selection).appServer;
3441
- return {
3442
- params,
3443
- options,
3444
- attemptStartedAt,
3445
- profilerEnabled,
3446
- codexModelCallTrace,
3447
- codexModelContentCapture,
3448
- codexModelCallId,
3449
- fastModeAutoStartedAtMs,
3450
- fastModeAutoProgressState,
3451
- preDynamicStartupStages,
3452
- attemptClientFactory,
3453
- runtimeArtifactRequest,
3454
- pluginConfig,
3455
- computerUseConfig,
3456
- sessionAgentId,
3457
- policyAgentId,
3458
- resolvedWorkspace,
3459
- sandboxSessionKey,
3460
- contextSessionKey,
3461
- sandbox,
3462
- agentDir,
3463
- shellEnvironment,
3464
- disableLoginShell,
3465
- bindingIdentity,
3466
- bindingStore,
3467
- activeContextEngine,
3468
- isInactiveThreadBootstrapBinding,
3469
- usesSupervisionConnection,
3470
- startupAuthProfileId,
3471
- startupAuthRequirement: preparedAuthRoute?.authRequirement,
3472
- startupPreparedAuth,
3473
- startupClientAuthProfileId,
3474
- effectiveWorkspace,
3475
- effectiveCwd,
3476
- appServer,
3477
- sessionPermissionPolicy,
3478
- nativeHookRelayEvents,
3479
- runAbortController,
3480
- terminalState,
3481
- abortExplicitly,
3482
- abortFromUpstream,
3483
- resolveReviewerPolicyContext,
3484
- resolveRuntimeOptionsForCurrentBinding,
3485
- mutable,
3486
- initialStartupBindingHadInactiveThreadBootstrap,
3487
- initialInactiveThreadBootstrapBindingForcedFreshStart
3488
- };
3489
3718
  }
3490
3719
  //#endregion
3491
3720
  //#region extensions/codex/src/app-server/run-attempt-state.ts
3492
- async function clearCodexBindingAfterInvalidImagePayload(bindingStore, identity, fields) {
3493
- const currentBinding = await bindingStore.read(identity);
3721
+ async function clearCodexBindingAfterInvalidImagePayload(bindingStore, identity, fields, expected) {
3722
+ const currentBinding = bindingStore.read(identity);
3494
3723
  const expectedThreadId = fields.threadId ?? currentBinding?.threadId;
3495
3724
  if (!expectedThreadId) return;
3496
3725
  if (currentBinding && currentBinding.threadId !== expectedThreadId) {
@@ -3500,8 +3729,8 @@ async function clearCodexBindingAfterInvalidImagePayload(bindingStore, identity,
3500
3729
  });
3501
3730
  return;
3502
3731
  }
3503
- if (currentBinding?.connectionScope === "supervision") {
3504
- embeddedAgentLog.warn("codex app-server image payload error detected for supervised thread; preserving native binding", fields);
3732
+ if (expected || currentBinding?.connectionScope === "supervision") {
3733
+ embeddedAgentLog.warn("codex app-server image payload error detected for native-owned thread; preserving binding", fields);
3505
3734
  return;
3506
3735
  }
3507
3736
  embeddedAgentLog.warn("codex app-server image payload error detected; clearing thread binding", fields);
@@ -3510,16 +3739,6 @@ async function clearCodexBindingAfterInvalidImagePayload(bindingStore, identity,
3510
3739
  threadId: expectedThreadId
3511
3740
  });
3512
3741
  }
3513
- async function markCodexAppServerBindingCoveredThroughTurn(params) {
3514
- await params.bindingStore.mutate(params.identity, {
3515
- kind: "patch",
3516
- threadId: params.threadId,
3517
- patch: {
3518
- historyCoveredThrough: (/* @__PURE__ */ new Date()).toISOString(),
3519
- ...params.continuityCalibration ? { continuityCalibration: params.continuityCalibration } : {}
3520
- }
3521
- });
3522
- }
3523
3742
  function isNonEmptyString(value) {
3524
3743
  return typeof value === "string" && value.length > 0;
3525
3744
  }
@@ -3556,10 +3775,14 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
3556
3775
  };
3557
3776
  const readFencedHistory = async () => {
3558
3777
  const transcriptReadFence = params.userTurnTranscriptRecorder?.getAdmissionReceipt();
3559
- return await readMirroredSessionHistoryMessages({
3778
+ const messages = await readMirroredSessionHistoryMessages({
3560
3779
  ...activeTranscriptTarget,
3780
+ signal: connection.runAbortController.signal,
3561
3781
  ...transcriptReadFence ? { admission: transcriptReadFence } : {}
3562
3782
  });
3783
+ connection.runAbortController.signal.throwIfAborted();
3784
+ connection.assertCurrent();
3785
+ return messages;
3563
3786
  };
3564
3787
  const historyState = { messages: !activeContextEngine && initialStartupBindingHadInactiveThreadBootstrap ? [] : await readFencedHistory() ?? [] };
3565
3788
  const hadSessionTranscriptState = historyState.messages.length > 0;
@@ -3574,6 +3797,10 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
3574
3797
  sessionKey: contextSessionKey,
3575
3798
  sessionId: params.sessionId,
3576
3799
  workspaceDir: params.workspaceDir,
3800
+ ...!usesSupervisionConnection && connection.mutable.startupBinding?.preserveNativeModel !== true ? {
3801
+ modelProviderId: params.provider,
3802
+ modelId: params.modelId
3803
+ } : {},
3577
3804
  trigger: params.trigger,
3578
3805
  ...buildAgentHookContextChannelFields({
3579
3806
  sessionKey: contextSessionKey,
@@ -3633,6 +3860,17 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
3633
3860
  });
3634
3861
  const agentWorkspaceDeveloperInstructions = workspaceBootstrapContext.threadDeveloperInstructions ? connection.mutable.startupBinding?.agentWorkspaceDeveloperInstructions ?? workspaceBootstrapContext.threadDeveloperInstructions : void 0;
3635
3862
  const baseDeveloperInstructions = joinPresentSections(buildDeveloperInstructions(runtimeParams, { dynamicTools: toolBridge.availableSpecs }), agentWorkspaceDeveloperInstructions);
3863
+ const watchedSessionsContext = buildCodexWatchedSessionsContext({
3864
+ attempt: runtimeParams,
3865
+ dynamicTools: toolBridge.availableSpecs,
3866
+ sessionKey: contextSessionKey,
3867
+ sandboxed: sandbox?.enabled === true
3868
+ });
3869
+ const buildOpenClawPromptContext = (includeWorkspaceReferences) => buildCodexOpenClawPromptContext({
3870
+ params: runtimeParams,
3871
+ workspacePromptContext: includeWorkspaceReferences ? workspaceBootstrapContext.promptContext : void 0,
3872
+ watchedSessionsContext
3873
+ });
3636
3874
  return {
3637
3875
  runtime,
3638
3876
  attemptTools,
@@ -3645,16 +3883,7 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
3645
3883
  workspaceBootstrapContext,
3646
3884
  agentWorkspaceDeveloperInstructions,
3647
3885
  baseDeveloperInstructions,
3648
- openClawPromptContext: buildCodexOpenClawPromptContext({
3649
- params: runtimeParams,
3650
- workspacePromptContext: workspaceBootstrapContext.promptContext,
3651
- watchedSessionsContext: buildCodexWatchedSessionsContext({
3652
- attempt: runtimeParams,
3653
- dynamicTools: toolBridge.availableSpecs,
3654
- sessionKey: contextSessionKey,
3655
- sandboxed: sandbox?.enabled === true
3656
- })
3657
- }),
3886
+ buildOpenClawPromptContext,
3658
3887
  skillsCollaborationInstructions: renderCodexSkillsCollaborationInstructions({
3659
3888
  attempt: runtimeParams,
3660
3889
  skillsPrompt: params.skillsSnapshot?.prompt
@@ -3901,6 +4130,7 @@ function recordCodexTrajectoryCompletion(recorder, params) {
3901
4130
  yieldDetected: params.yieldDetected ?? false,
3902
4131
  aborted: terminal.aborted,
3903
4132
  promptError: normalizeCodexTrajectoryError(terminal.promptError),
4133
+ ...terminal.settlementWarning ? { settlementWarning: terminal.settlementWarning } : {},
3904
4134
  usage: params.result.attemptUsage,
3905
4135
  assistantTexts: params.result.assistantTexts,
3906
4136
  messagesSnapshot: params.result.messagesSnapshot
@@ -3940,265 +4170,395 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
3940
4170
  const { effectiveRuntimeProviderId, effectiveRuntimeModelId } = runtime;
3941
4171
  const { params, terminalState, runAbortController, activeContextEngine, bindingStore, bindingIdentity, appServer, usesSupervisionConnection, sessionAgentId, contextSessionKey, effectiveCwd, agentDir, attemptStartedAt, startupAuthProfileId } = connection;
3942
4172
  const { toolBridge, toolState } = attemptTools;
3943
- const { state, completion, deadlines } = turnRuntime;
4173
+ const canClearBindingForRecovery = (operation) => {
4174
+ if (params.expectedSessionRuntimeOwnership) {
4175
+ embeddedAgentLog.warn("codex app-server preserved native binding instead of recovery rotation", {
4176
+ threadId: resourceState.thread.threadId,
4177
+ operation
4178
+ });
4179
+ return false;
4180
+ }
4181
+ assertCodexBindingMayBeReplaced(resourceState.thread, operation);
4182
+ return true;
4183
+ };
4184
+ const { state, completion, deadlines, settlementExpired } = turnRuntime;
3944
4185
  const { emitLifecycleTerminal, buildLifecycleTerminalMeta } = lifecycle;
3945
4186
  const { drainNotificationQueue } = notifications;
3946
4187
  const { codexModelCallDiagnostics } = requestRuntime;
3947
- const { activeTurnId, activeProjector, streamState, freezeRunTerminalOutcome, notifyUserMessagePersisted } = activeTurn;
4188
+ const { activeTurnId, activeProjector, runtimeModelSelection, streamState, freezeRunTerminalOutcome, notifyUserMessagePersisted } = activeTurn;
3948
4189
  await completion;
3949
- const abortGraceElapsed = createDeferred();
3950
- let settlementClosed = false;
3951
- let abortGraceTimer;
3952
- const beginAbortGrace = () => {
3953
- if (settlementClosed) return;
3954
- abortGraceTimer = setTimeout(() => abortGraceElapsed.resolve(), TURN_FINALIZE_DRAIN_ABORT_GRACE_MS);
3955
- abortGraceTimer.unref?.();
4190
+ const drainGraceElapsed = createDeferred();
4191
+ let settlementPhase = "active";
4192
+ let drainGraceTimer;
4193
+ const beginDrainGrace = () => {
4194
+ if (settlementPhase !== "active" || drainGraceTimer) return;
4195
+ drainGraceTimer = setTimeout(() => {
4196
+ settlementPhase = "expired";
4197
+ drainGraceElapsed.resolve();
4198
+ }, TURN_FINALIZE_DRAIN_ABORT_GRACE_MS);
4199
+ drainGraceTimer.unref?.();
3956
4200
  };
3957
4201
  const abortListener = addAbortListener(runAbortController.signal, () => {
3958
- state.abortCleanup.then(beginAbortGrace, beginAbortGrace);
4202
+ state.abortCleanup.then(beginDrainGrace, beginDrainGrace);
3959
4203
  });
3960
4204
  const closeProjection = () => {
3961
4205
  state.projectionClosed = true;
3962
4206
  return activeProjector.closeProjection();
3963
4207
  };
3964
- const settlement = drainNotificationQueue().then(closeProjection);
3965
- try {
3966
- await Promise.race([settlement, abortGraceElapsed.promise]);
3967
- if (runAbortController.signal.aborted) await state.abortCleanup;
3968
- } finally {
3969
- settlementClosed = true;
4208
+ const closeSettlement = () => {
4209
+ if (settlementPhase === "closed") return;
4210
+ settlementPhase = "closed";
3970
4211
  abortListener[Symbol.dispose]();
3971
- clearTimeout(abortGraceTimer);
4212
+ clearTimeout(drainGraceTimer);
3972
4213
  deadlines.dispose();
3973
- if (!state.projectionClosed) await resources.runCleanupStep("codex-transcript-checkpoint", closeProjection);
3974
- }
3975
- const result = activeProjector.buildResult(toolBridge.telemetry, { yieldDetected: toolState.yieldDetected });
3976
- const projectedTerminal = attemptTerminal.project(result.terminal);
3977
- const effectiveTimedOut = state.timeout !== void 0;
3978
- const isFinalAborted = () => terminalState.explicitCancellationObserved || !resourceState.executionDisconnectError && (projectedTerminal.aborted || runAbortController.signal.aborted && !state.clientClosedAbort);
3979
- const clientClosedPromptErrorForFinal = state.clientClosedPromptError;
3980
- let finalPromptError = resourceState.executionDisconnectError ?? clientClosedPromptErrorForFinal ?? (state.timeout?.kind === "settlement" ? "codex app-server terminal settlement timed out" : state.timeout?.kind === "execution" ? "codex app-server execution budget timed out" : projectedTerminal.promptError);
3981
- const finalPromptErrorMessage = typeof finalPromptError === "string" ? finalPromptError : finalPromptError instanceof Error ? finalPromptError.message : finalPromptError ? formatErrorMessage(finalPromptError) : void 0;
3982
- if (isInvalidCodexImagePayloadError(finalPromptErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(bindingStore, bindingIdentity, {
3983
- phase: "turn_completed",
3984
- threadId: resourceState.thread.threadId,
3985
- turnId: activeTurnId,
3986
- error: finalPromptErrorMessage
4214
+ };
4215
+ const settlement = drainNotificationQueue().then(async () => {
4216
+ await closeProjection();
4217
+ await activeProjector.settlement.drain();
4218
+ });
4219
+ const degradedSettlement = settlementExpired.then(() => {
4220
+ beginDrainGrace();
3987
4221
  });
3988
- if (resourceState.thread.connectionScope !== "supervision" && shouldUseFreshCodexThreadAfterContextEngineOverflow({
3989
- error: finalPromptError,
3990
- contextEngineActive: Boolean(activeContextEngine),
3991
- thread: resourceState.thread
3992
- })) {
3993
- embeddedAgentLog.warn("codex app-server context-engine turn overflowed after resume; clearing thread binding for recovery", {
4222
+ let projectionDrained = false;
4223
+ try {
4224
+ try {
4225
+ projectionDrained = await Promise.race([
4226
+ settlement.then(() => true),
4227
+ drainGraceElapsed.promise.then(() => false),
4228
+ degradedSettlement.then(() => false)
4229
+ ]);
4230
+ if (runAbortController.signal.aborted) await state.abortCleanup;
4231
+ } finally {
4232
+ if (!state.projectionClosed) await resources.runCleanupStep("codex-transcript-checkpoint", closeProjection);
4233
+ }
4234
+ const result = activeProjector.buildResult(toolBridge.telemetry, { yieldDetected: toolState.yieldDetected });
4235
+ const projectedTerminal = attemptTerminal.project(result.terminal);
4236
+ const isFinalAborted = () => terminalState.explicitCancellationObserved || !resourceState.executionDisconnectError && (projectedTerminal.aborted || runAbortController.signal.aborted && !state.clientClosedAbort);
4237
+ const currentPromptError = (fallback) => resourceState.executionDisconnectError ?? state.clientClosedPromptError ?? (state.timeout ? `codex app-server ${state.timeout.kind === "execution" ? "execution budget" : "terminal settlement"} timed out` : fallback);
4238
+ let enrichedPromptError = currentPromptError(projectedTerminal.promptError);
4239
+ const enrichedPromptErrorMessage = typeof enrichedPromptError === "string" ? enrichedPromptError : enrichedPromptError instanceof Error ? enrichedPromptError.message : enrichedPromptError ? formatErrorMessage(enrichedPromptError) : void 0;
4240
+ if (isInvalidCodexImagePayloadError(enrichedPromptErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(bindingStore, bindingIdentity, {
4241
+ phase: "turn_completed",
4242
+ threadId: resourceState.thread.threadId,
4243
+ turnId: activeTurnId,
4244
+ error: enrichedPromptErrorMessage
4245
+ }, params.expectedSessionRuntimeOwnership);
4246
+ if (resourceState.thread.connectionScope !== "supervision" && shouldUseFreshCodexThreadAfterContextEngineOverflow({
4247
+ error: enrichedPromptError,
4248
+ contextEngineActive: Boolean(activeContextEngine),
4249
+ thread: resourceState.thread
4250
+ }) && canClearBindingForRecovery("clearing a native context after overflow")) {
4251
+ embeddedAgentLog.warn("codex app-server context-engine turn overflowed after resume; clearing thread binding for recovery", {
4252
+ threadId: resourceState.thread.threadId,
4253
+ turnId: activeTurnId,
4254
+ error: enrichedPromptErrorMessage
4255
+ });
4256
+ await bindingStore.mutate(bindingIdentity, {
4257
+ kind: "clear",
4258
+ threadId: resourceState.thread.threadId
4259
+ });
4260
+ }
4261
+ const refreshedUsageLimitPromptError = await refreshCodexUsageLimitPromptError({
4262
+ client: resourceState.client,
4263
+ message: enrichedPromptErrorMessage,
4264
+ timeoutMs: appServer.requestTimeoutMs,
4265
+ signal: runAbortController.signal
4266
+ });
4267
+ if (refreshedUsageLimitPromptError) {
4268
+ await markCodexAuthProfileBlockedFromRateLimits({
4269
+ params,
4270
+ authProfileId: startupAuthProfileId,
4271
+ rateLimits: refreshedUsageLimitPromptError.rateLimitsForProfile
4272
+ });
4273
+ enrichedPromptError = new CodexUsageLimitPromptError(refreshedUsageLimitPromptError.message);
4274
+ } else if (enrichedPromptError instanceof CodexUsageLimitPromptError && state.rateLimitsRevisionBeforeLastTurnStart !== void 0 && readCodexRateLimitsRevision(resourceState.client) > state.rateLimitsRevisionBeforeLastTurnStart) await markCodexAuthProfileBlockedFromRateLimits({
4275
+ params,
4276
+ authProfileId: startupAuthProfileId,
4277
+ rateLimits: readRecentCodexRateLimits(resourceState.client)
4278
+ });
4279
+ const projectTerminalOutcome = () => {
4280
+ const effectiveTimedOut = state.timeout !== void 0;
4281
+ const clientClosedPromptErrorForFinal = state.clientClosedPromptError;
4282
+ const finalPromptError = currentPromptError(enrichedPromptError);
4283
+ const finalPromptErrorSource = effectiveTimedOut || clientClosedPromptErrorForFinal ? "prompt" : projectedTerminal.promptErrorSource;
4284
+ const codexAppServerFailureKind = clientClosedPromptErrorForFinal ? "client_closed_before_turn_completed" : state.timeout?.kind === "settlement" ? "turn_settlement_timeout" : void 0;
4285
+ const replayBlockedReason = codexAppServerFailureKind ? resolveCodexAppServerReplayBlockedReason(result) : void 0;
4286
+ const promptTimeoutOutcome = buildCodexAppServerPromptTimeoutOutcome(state.timeout);
4287
+ const failureDiagnostics = codexAppServerFailureKind === "client_closed_before_turn_completed" && state.clientClosedDiagnostic ? { transportError: state.clientClosedDiagnostic } : state.timeout?.kind === "settlement" ? { timeoutMs: state.timeout.timeoutMs } : void 0;
4288
+ const codexAppServerFailure = codexAppServerFailureKind ? {
4289
+ kind: codexAppServerFailureKind,
4290
+ transport: appServer.start.transport,
4291
+ threadId: resourceState.thread.threadId,
4292
+ turnId: activeTurnId,
4293
+ replaySafe: codexAppServerFailureKind === "client_closed_before_turn_completed" && replayBlockedReason === void 0,
4294
+ ...replayBlockedReason ? { replayBlockedReason } : {},
4295
+ ...failureDiagnostics ? { diagnostics: failureDiagnostics } : {}
4296
+ } : void 0;
4297
+ const finalAborted = isFinalAborted();
4298
+ if (finalAborted && result.attemptUsage) result.attemptUsage = {
4299
+ ...result.attemptUsage,
4300
+ contextUsage: { state: "unavailable" }
4301
+ };
4302
+ const completedTurnStatus = activeProjector.getCompletedTurnStatus();
4303
+ const locallyCompletedTurn = state.completed && state.localCompletionRequested && !state.timeout && clientClosedPromptErrorForFinal === void 0;
4304
+ const turnSucceeded = !finalAborted && !effectiveTimedOut && (finalPromptError === null || finalPromptError === void 0) && (completedTurnStatus === "completed" || locallyCompletedTurn);
4305
+ if (toolBridge.telemetry.messagingToolSentTargets.some((target) => target.sourceReplyFinal === true)) result.agentHarnessResultClassification = void 0;
4306
+ const attemptSucceeded = turnSucceeded && result.agentHarnessResultClassification === void 0;
4307
+ result.terminal = attemptTerminal.normalize({
4308
+ settlementWarning: state.settlementWarning,
4309
+ timedOut: effectiveTimedOut,
4310
+ aborted: finalAborted,
4311
+ promptError: finalPromptError,
4312
+ promptErrorSource: finalPromptErrorSource
4313
+ });
4314
+ for (const message of [
4315
+ result.lastAssistant,
4316
+ result.currentAttemptAssistant,
4317
+ result.messagesSnapshot.find((candidate) => readMirrorIdentity(candidate) === `${activeTurnId}:assistant`)
4318
+ ]) if (message?.role === "assistant") {
4319
+ if (!message.diagnostics?.some((diagnostic) => diagnostic.type === "provider_refusal") || finalAborted || finalPromptError) {
4320
+ message.stopReason = finalAborted ? "aborted" : finalPromptError ? "error" : "stop";
4321
+ message.errorMessage = finalPromptError ? formatErrorMessage(finalPromptError) : void 0;
4322
+ }
4323
+ }
4324
+ return {
4325
+ effectiveTimedOut,
4326
+ finalPromptError,
4327
+ codexAppServerFailure,
4328
+ promptTimeoutOutcome,
4329
+ finalAborted,
4330
+ turnSucceeded,
4331
+ attemptSucceeded,
4332
+ completedTurnStatus
4333
+ };
4334
+ };
4335
+ projectTerminalOutcome();
4336
+ const unavailableMirror = {
4337
+ assistantTranscriptOwned: false,
4338
+ mirroredMessages: []
4339
+ };
4340
+ let mirrorOutcome = unavailableMirror;
4341
+ const mirrorFinal = () => {
4342
+ const warning = state.settlementWarning;
4343
+ const mirrorTerminal = projectTerminalOutcome();
4344
+ state.pendingSettlementStage = "transcript/mirror";
4345
+ return codexTranscriptMirrorRuntime.mirrorBestEffort({
4346
+ assertWriteCurrent: () => {
4347
+ if (settlementPhase !== "active" || state.settlementWarning !== warning) throw new Error("Codex transcript settlement is no longer active");
4348
+ const current = projectTerminalOutcome();
4349
+ if (current.finalAborted !== mirrorTerminal.finalAborted || current.effectiveTimedOut !== mirrorTerminal.effectiveTimedOut || current.finalPromptError !== mirrorTerminal.finalPromptError) throw new Error("Codex transcript terminal outcome changed before write");
4350
+ },
4351
+ params,
4352
+ settlementWarning: warning,
4353
+ agentId: sessionAgentId,
4354
+ notifyUserMessagePersisted,
4355
+ result,
4356
+ sessionKey: contextSessionKey,
4357
+ cwd: effectiveCwd,
4358
+ threadId: resourceState.thread.threadId,
4359
+ turnId: activeTurnId
4360
+ });
4361
+ };
4362
+ try {
4363
+ if (projectionDrained && settlementPhase === "active" && !state.settlementWarning) mirrorOutcome = await Promise.race([
4364
+ mirrorFinal(),
4365
+ drainGraceElapsed.promise.then(() => unavailableMirror),
4366
+ degradedSettlement.then(() => unavailableMirror)
4367
+ ]);
4368
+ if (state.settlementWarning && !runAbortController.signal.aborted) {
4369
+ mirrorOutcome = await Promise.race([mirrorFinal(), drainGraceElapsed.promise.then(() => unavailableMirror)]);
4370
+ if (mirrorOutcome === unavailableMirror) trajectoryRecorder?.recordEvent("turn.settlement_persistence_unavailable", {
4371
+ pendingStage: "transcript/mirror",
4372
+ threadId: resourceState.thread.threadId,
4373
+ turnId: activeTurnId
4374
+ });
4375
+ }
4376
+ if (toolState.yieldMessage && projectTerminalOutcome().turnSucceeded) {
4377
+ state.pendingSettlementStage = "transcript/yield-context";
4378
+ await Promise.race([
4379
+ appendSessionYieldContext({
4380
+ ...activeTranscriptTarget.sessionTarget,
4381
+ agentId: activeTranscriptTarget.agentId,
4382
+ sessionId: activeTranscriptTarget.sessionId,
4383
+ sessionKey: activeTranscriptTarget.sessionKey,
4384
+ config: params.config,
4385
+ message: toolState.yieldMessage,
4386
+ assertCurrent: () => {
4387
+ connection.assertCurrent();
4388
+ if (settlementPhase !== "active" || !projectTerminalOutcome().turnSucceeded) throw new Error("Codex yield settlement is no longer active");
4389
+ }
4390
+ }),
4391
+ drainGraceElapsed.promise,
4392
+ degradedSettlement
4393
+ ]);
4394
+ }
4395
+ if (runAbortController.signal.aborted) await state.abortCleanup;
4396
+ } finally {
4397
+ closeSettlement();
4398
+ }
4399
+ const { effectiveTimedOut, finalPromptError, codexAppServerFailure, promptTimeoutOutcome, finalAborted, turnSucceeded, attemptSucceeded, completedTurnStatus } = projectTerminalOutcome();
4400
+ terminalState.settledTurnStatus = turnSucceeded ? "completed" : completedTurnStatus === "failed" && !finalAborted && !effectiveTimedOut && !state.clientClosedPromptError && !resourceState.executionDisconnectError ? "failed" : void 0;
4401
+ terminalState.sharedAbortAllowedAfterTerminalOutcome = shouldKeepCodexSharedAbortOpen({
4402
+ trigger: params.trigger,
4403
+ result,
4404
+ attemptSucceeded,
4405
+ explicitCancellationObserved: terminalState.explicitCancellationObserved
4406
+ });
4407
+ freezeRunTerminalOutcome();
4408
+ const modelCallFailureKind = classifyCodexModelCallFailureKind({
4409
+ error: finalPromptError,
4410
+ timedOut: effectiveTimedOut,
4411
+ runAborted: finalAborted,
4412
+ abortReason: terminalState.explicitCancellationReason ?? runAbortController.signal.reason,
4413
+ clientClosedAbort: state.clientClosedAbort,
4414
+ formatError: formatErrorMessage
4415
+ }) ?? (finalAborted ? "aborted" : void 0);
4416
+ if (modelCallFailureKind) codexModelCallDiagnostics.emitError(finalPromptError ?? "codex app-server attempt interrupted", { failureKind: modelCallFailureKind });
4417
+ else if (finalPromptError) codexModelCallDiagnostics.emitError(finalPromptError);
4418
+ else codexModelCallDiagnostics.emitCompleted(result);
4419
+ const { assistantTranscriptOwned, assistantTranscriptIdempotencyKey, terminalAnchor } = mirrorOutcome;
4420
+ const settledTurnFinalizationContext = result.assistantTexts.every((text) => !text.trim()) && result.messagesSnapshot.some((message) => message.role === "toolResult") && (!finalPromptError || activeProjector.settledTurnFailureFinalizationAllowed) ? (!usesSupervisionConnection ? await captureCodexSettledTurnFinalizationContext({
4421
+ ...activeTranscriptTarget,
4422
+ model: resourceState.thread.model,
4423
+ modelProvider: resourceState.thread.modelProvider,
4424
+ authProfileId: startupAuthProfileId,
4425
+ mirroredMessages: mirrorOutcome.mirroredMessages,
4426
+ settledMessages: result.messagesSnapshot,
4427
+ turnId: activeTurnId,
4428
+ signal: params.abortSignal,
4429
+ assertActive: connection.assertCurrent
4430
+ }) : void 0) ?? Object.freeze({ source: "unavailable" }) : void 0;
4431
+ if (settledTurnFinalizationContext?.source === "unavailable") embeddedAgentLog.warn("codex settled-turn finalization context is unavailable", {
4432
+ runId: params.runId,
3994
4433
  threadId: resourceState.thread.threadId,
3995
4434
  turnId: activeTurnId,
3996
- error: finalPromptErrorMessage
4435
+ reason: usesSupervisionConnection ? "native_auth_finalization_unsupported" : "context_unavailable"
3997
4436
  });
3998
- await bindingStore.mutate(bindingIdentity, {
3999
- kind: "clear",
4000
- threadId: resourceState.thread.threadId
4437
+ runAgentHarnessLlmOutputHook({
4438
+ event: {
4439
+ runId: params.runId,
4440
+ sessionId: params.sessionId,
4441
+ provider: usesSupervisionConnection ? resourceState.thread.modelProvider ?? effectiveRuntimeProviderId : params.provider,
4442
+ model: usesSupervisionConnection ? resourceState.thread.model ?? effectiveRuntimeModelId : params.modelId,
4443
+ ...hookContextWindowFields,
4444
+ resolvedRef: usesSupervisionConnection ? `${resourceState.thread.modelProvider ?? effectiveRuntimeProviderId}/${resourceState.thread.model ?? effectiveRuntimeModelId}` : params.runtimePlan?.observability.resolvedRef ?? `${params.provider}/${params.modelId}`,
4445
+ ...!usesSupervisionConnection && params.runtimePlan?.observability.harnessId ? { harnessId: params.runtimePlan.observability.harnessId } : {},
4446
+ assistantTexts: result.assistantTexts,
4447
+ ...result.lastAssistant ? { lastAssistant: result.lastAssistant } : {},
4448
+ ...result.attemptUsage ? { usage: result.attemptUsage } : {}
4449
+ },
4450
+ ctx: hookContext,
4451
+ hookRunner
4001
4452
  });
4002
- }
4003
- const refreshedUsageLimitPromptError = await refreshCodexUsageLimitPromptError({
4004
- client: resourceState.client,
4005
- message: finalPromptErrorMessage,
4006
- timeoutMs: appServer.requestTimeoutMs,
4007
- signal: runAbortController.signal
4008
- });
4009
- if (refreshedUsageLimitPromptError) {
4010
- await markCodexAuthProfileBlockedFromRateLimits({
4011
- params,
4012
- authProfileId: startupAuthProfileId,
4013
- rateLimits: refreshedUsageLimitPromptError.rateLimitsForProfile
4453
+ const providerRefusal = result.currentAttemptAssistant?.diagnostics?.some((diagnostic) => diagnostic.type === "provider_refusal");
4454
+ await runCodexAgentEndHook(params, {
4455
+ skillExperienceReviewSource: providerRefusal ? void 0 : terminalAnchor,
4456
+ event: {
4457
+ messages: result.messagesSnapshot,
4458
+ success: !finalAborted && !finalPromptError,
4459
+ ...finalPromptError ? { error: formatErrorMessage(finalPromptError) } : {},
4460
+ durationMs: Date.now() - attemptStartedAt
4461
+ },
4462
+ ctx: {
4463
+ ...hookContext,
4464
+ modelProviderId: resourceState.thread.modelProvider ?? effectiveRuntimeProviderId,
4465
+ modelId: resourceState.thread.model ?? effectiveRuntimeModelId,
4466
+ authProfileId: resourceState.thread.authProfileId ?? startupAuthProfileId,
4467
+ modelIterations: result.modelIterations ?? 0,
4468
+ skillWorkshopAvailable: flattenCodexDynamicToolFunctions(attemptTools.toolBridge.availableSpecs).some((tool) => tool.name === "skill_workshop"),
4469
+ compacted: (result.compactionCount ?? 0) > 0,
4470
+ senderId: params.senderId ?? void 0,
4471
+ foregroundPromptContext: buildEmbeddedForegroundPromptContext({
4472
+ ...params,
4473
+ agentId: sessionAgentId
4474
+ }, agentDir)
4475
+ },
4476
+ hookRunner
4014
4477
  });
4015
- finalPromptError = createCodexUsageLimitPromptError(refreshedUsageLimitPromptError.message);
4016
- } else if (isCodexUsageLimitPromptError(finalPromptError) && state.rateLimitsRevisionBeforeLastTurnStart !== void 0 && readCodexRateLimitsRevision(resourceState.client) > state.rateLimitsRevisionBeforeLastTurnStart) await markCodexAuthProfileBlockedFromRateLimits({
4017
- params,
4018
- authProfileId: startupAuthProfileId,
4019
- rateLimits: readRecentCodexRateLimits(resourceState.client)
4020
- });
4021
- finalPromptError = resourceState.executionDisconnectError ?? finalPromptError;
4022
- const finalPromptErrorSource = effectiveTimedOut || clientClosedPromptErrorForFinal ? "prompt" : projectedTerminal.promptErrorSource;
4023
- const codexAppServerFailureKind = clientClosedPromptErrorForFinal ? "client_closed_before_turn_completed" : state.timeout?.kind === "settlement" ? "turn_settlement_timeout" : void 0;
4024
- const replayBlockedReason = codexAppServerFailureKind ? resolveCodexAppServerReplayBlockedReason(result) : void 0;
4025
- const promptTimeoutOutcome = buildCodexAppServerPromptTimeoutOutcome(state.timeout);
4026
- const failureDiagnostics = codexAppServerFailureKind === "client_closed_before_turn_completed" && state.clientClosedDiagnostic ? { transportError: state.clientClosedDiagnostic } : state.timeout?.kind === "settlement" ? { timeoutMs: state.timeout.timeoutMs } : void 0;
4027
- const codexAppServerFailure = codexAppServerFailureKind ? {
4028
- kind: codexAppServerFailureKind,
4029
- transport: appServer.start.transport,
4030
- threadId: resourceState.thread.threadId,
4031
- turnId: activeTurnId,
4032
- replaySafe: codexAppServerFailureKind === "client_closed_before_turn_completed" && replayBlockedReason === void 0,
4033
- ...replayBlockedReason ? { replayBlockedReason } : {},
4034
- ...failureDiagnostics ? { diagnostics: failureDiagnostics } : {}
4035
- } : void 0;
4036
- const finalAborted = isFinalAborted();
4037
- const completedTurnStatus = activeProjector.getCompletedTurnStatus();
4038
- const locallyCompletedTurn = state.completed && state.localCompletionRequested && !state.timeout && clientClosedPromptErrorForFinal === void 0;
4039
- const turnSucceeded = !finalAborted && !effectiveTimedOut && (finalPromptError === null || finalPromptError === void 0) && (completedTurnStatus === "completed" || locallyCompletedTurn);
4040
- if (toolBridge.telemetry.messagingToolSentTargets.some((target) => target.sourceReplyFinal === true)) result.agentHarnessResultClassification = void 0;
4041
- const attemptSucceeded = turnSucceeded && result.agentHarnessResultClassification === void 0;
4042
- terminalState.turnSucceeded = turnSucceeded;
4043
- terminalState.sharedAbortAllowedAfterTerminalOutcome = shouldKeepCodexSharedAbortOpen({
4044
- trigger: params.trigger,
4045
- result,
4046
- attemptSucceeded,
4047
- explicitCancellationObserved: terminalState.explicitCancellationObserved
4048
- });
4049
- freezeRunTerminalOutcome();
4050
- const modelCallFailureKind = classifyCodexModelCallFailureKind({
4051
- error: finalPromptError,
4052
- timedOut: effectiveTimedOut,
4053
- runAborted: finalAborted,
4054
- abortReason: terminalState.explicitCancellationReason ?? runAbortController.signal.reason,
4055
- clientClosedAbort: state.clientClosedAbort,
4056
- formatError: formatErrorMessage
4057
- }) ?? (finalAborted ? "aborted" : void 0);
4058
- if (modelCallFailureKind) codexModelCallDiagnostics.emitError(finalPromptError ?? "codex app-server attempt interrupted", { failureKind: modelCallFailureKind });
4059
- else if (finalPromptError) codexModelCallDiagnostics.emitError(finalPromptError);
4060
- else codexModelCallDiagnostics.emitCompleted(result);
4061
- const mirrorOutcome = await codexTranscriptMirrorRuntime.mirrorBestEffort({
4062
- params,
4063
- agentId: sessionAgentId,
4064
- notifyUserMessagePersisted,
4065
- result,
4066
- sessionKey: contextSessionKey,
4067
- cwd: effectiveCwd,
4068
- threadId: resourceState.thread.threadId,
4069
- turnId: activeTurnId
4070
- });
4071
- const { assistantTranscriptOwned, assistantTranscriptIdempotencyKey, terminalAnchor } = mirrorOutcome;
4072
- const settledTurnFinalizationContext = result.assistantTexts.every((text) => !text.trim()) && result.messagesSnapshot.some((message) => message.role === "toolResult") && (!finalPromptError || activeProjector.settledTurnFailureFinalizationAllowed) ? await captureCodexSettledTurnFinalizationContext({
4073
- ...activeTranscriptTarget,
4074
- mirroredMessages: mirrorOutcome.mirroredMessages,
4075
- settledMessages: result.messagesSnapshot,
4076
- turnId: activeTurnId
4077
- }) ?? Object.freeze({ source: "unavailable" }) : void 0;
4078
- if (settledTurnFinalizationContext?.source === "unavailable") embeddedAgentLog.warn("codex settled-turn finalization context is unavailable", {
4079
- threadId: resourceState.thread.threadId,
4080
- turnId: activeTurnId
4081
- });
4082
- runAgentHarnessLlmOutputHook({
4083
- event: {
4084
- runId: params.runId,
4085
- sessionId: params.sessionId,
4086
- provider: usesSupervisionConnection ? resourceState.thread.modelProvider ?? effectiveRuntimeProviderId : params.provider,
4087
- model: usesSupervisionConnection ? resourceState.thread.model ?? effectiveRuntimeModelId : params.modelId,
4088
- ...hookContextWindowFields,
4089
- resolvedRef: usesSupervisionConnection ? `${resourceState.thread.modelProvider ?? effectiveRuntimeProviderId}/${resourceState.thread.model ?? effectiveRuntimeModelId}` : params.runtimePlan?.observability.resolvedRef ?? `${params.provider}/${params.modelId}`,
4090
- ...!usesSupervisionConnection && params.runtimePlan?.observability.harnessId ? { harnessId: params.runtimePlan.observability.harnessId } : {},
4091
- assistantTexts: result.assistantTexts,
4092
- ...result.lastAssistant ? { lastAssistant: result.lastAssistant } : {},
4093
- ...result.attemptUsage ? { usage: result.attemptUsage } : {}
4094
- },
4095
- ctx: hookContext,
4096
- hookRunner
4097
- });
4098
- await runCodexAgentEndHook(params, {
4099
- event: {
4100
- messages: result.messagesSnapshot,
4101
- success: !finalAborted && !finalPromptError,
4102
- ...finalPromptError ? { error: formatErrorMessage(finalPromptError) } : {},
4103
- durationMs: Date.now() - attemptStartedAt
4104
- },
4105
- ctx: {
4106
- ...hookContext,
4107
- modelProviderId: resourceState.thread.modelProvider ?? effectiveRuntimeProviderId,
4108
- modelId: resourceState.thread.model ?? effectiveRuntimeModelId,
4109
- authProfileId: resourceState.thread.authProfileId ?? startupAuthProfileId,
4110
- modelIterations: result.modelIterations ?? 0,
4111
- skillWorkshopAvailable: flattenCodexDynamicToolFunctions(attemptTools.toolBridge.availableSpecs).some((tool) => tool.name === "skill_workshop"),
4112
- compacted: (result.compactionCount ?? 0) > 0,
4113
- senderId: params.senderId ?? void 0,
4114
- foregroundPromptContext: buildEmbeddedForegroundPromptContext({
4115
- ...params,
4116
- agentId: sessionAgentId
4117
- }, agentDir)
4118
- },
4119
- hookRunner
4120
- });
4121
- state.shouldDelayNativeHookRelayUnregister = completedTurnStatus === "completed" && !effectiveTimedOut && !runAbortController.signal.aborted && !finalAborted && !finalPromptError;
4122
- if (state.shouldDelayNativeHookRelayUnregister) try {
4123
- await markCodexAppServerBindingCoveredThroughTurn({
4124
- bindingStore,
4125
- identity: bindingIdentity,
4126
- threadId: resourceState.thread.threadId,
4127
- continuityCalibration: context.promptState.noEngineContinuityProjectionApplied ? buildCodexContinuityCalibration({
4478
+ state.shouldDelayNativeHookRelayUnregister = completedTurnStatus === "completed" && !effectiveTimedOut && !runAbortController.signal.aborted && !finalAborted && !finalPromptError;
4479
+ if (state.shouldDelayNativeHookRelayUnregister) try {
4480
+ const continuityCalibration = context.promptState.noEngineContinuityProjectionApplied ? buildCodexContinuityCalibration({
4128
4481
  promptChars: prompt.turnState.codexTurnPromptText.length,
4129
- inputTokens: (result.attemptUsage?.input ?? 0) + (result.attemptUsage?.cacheRead ?? 0) + (result.attemptUsage?.cacheWrite ?? 0)
4130
- }) : void 0
4482
+ inputTokens: result.attemptUsage?.contextUsage?.state === "available" ? result.attemptUsage.contextUsage.promptTokens ?? 0 : 0
4483
+ }) : void 0;
4484
+ await bindingStore.mutate(bindingIdentity, {
4485
+ kind: "patch",
4486
+ threadId: resourceState.thread.threadId,
4487
+ patch: {
4488
+ historyCoveredThrough: (/* @__PURE__ */ new Date()).toISOString(),
4489
+ ...continuityCalibration ? { continuityCalibration } : {}
4490
+ }
4491
+ }, connection.assertCurrent);
4492
+ } catch (error) {
4493
+ if (resourceState.thread.connectionScope === "supervision") throw error;
4494
+ if (canClearBindingForRecovery("clearing native coverage after a completed turn")) {
4495
+ if (!await bindingStore.mutate(bindingIdentity, {
4496
+ kind: "clear",
4497
+ threadId: resourceState.thread.threadId
4498
+ }, connection.assertCurrent)) throw error;
4499
+ embeddedAgentLog.warn("codex app-server binding coverage update failed after completed turn; cleared stale binding", {
4500
+ threadId: resourceState.thread.threadId,
4501
+ turnId: activeTurnId,
4502
+ error
4503
+ });
4504
+ }
4505
+ }
4506
+ recordCodexTrajectoryCompletion(trajectoryRecorder, {
4507
+ attempt: params,
4508
+ result,
4509
+ threadId: resourceState.thread.threadId,
4510
+ turnId: activeTurnId,
4511
+ timedOut: effectiveTimedOut,
4512
+ yieldDetected: toolState.yieldDetected
4131
4513
  });
4132
- } catch (error) {
4133
- if (resourceState.thread.connectionScope === "supervision") throw error;
4134
- if (!await bindingStore.mutate(bindingIdentity, {
4135
- kind: "clear",
4136
- threadId: resourceState.thread.threadId
4137
- })) throw error;
4138
- embeddedAgentLog.warn("codex app-server binding coverage update failed after completed turn; cleared stale binding", {
4514
+ trajectoryRecorder?.recordEvent("session.ended", {
4515
+ status: finalPromptError ? "error" : finalAborted || effectiveTimedOut ? "interrupted" : "success",
4139
4516
  threadId: resourceState.thread.threadId,
4140
4517
  turnId: activeTurnId,
4141
- error
4518
+ timedOut: effectiveTimedOut,
4519
+ yieldDetected: toolState.yieldDetected,
4520
+ promptError: normalizeCodexTrajectoryError(finalPromptError)
4521
+ });
4522
+ markTrajectoryEndRecorded();
4523
+ const terminalAssistantText = collectTerminalAssistantText(result);
4524
+ if (terminalAssistantText && (!streamState.eventEmitted || streamState.needsTerminalSnapshot) && !finalAborted && !finalPromptError) emitCodexAppServerEvent(params, {
4525
+ stream: "assistant",
4526
+ data: { text: terminalAssistantText }
4527
+ });
4528
+ emitLifecycleTerminal(finalPromptError ? {
4529
+ phase: "error",
4530
+ error: formatErrorMessage(finalPromptError),
4531
+ ...buildLifecycleTerminalMeta({
4532
+ aborted: finalAborted,
4533
+ timedOut: effectiveTimedOut
4534
+ })
4535
+ } : {
4536
+ phase: "end",
4537
+ ...buildLifecycleTerminalMeta({
4538
+ aborted: finalAborted,
4539
+ timedOut: effectiveTimedOut,
4540
+ yielded: toolState.yieldDetected
4541
+ })
4142
4542
  });
4543
+ const finalizedResult = Object.assign(result, {
4544
+ ...runtimeModelSelection ? { runtimeModelSelection } : {},
4545
+ ...toolState.yieldAcknowledgment ? { yieldAcknowledgment: toolState.yieldAcknowledgment } : {},
4546
+ ...codexAppServerFailure ? { codexAppServerFailure } : {},
4547
+ ...promptTimeoutOutcome ? { promptTimeoutOutcome } : {},
4548
+ ...assistantTranscriptOwned ? { assistantTranscriptOwned: true } : {},
4549
+ ...assistantTranscriptIdempotencyKey ? { assistantTranscriptIdempotencyKey } : {},
4550
+ ...terminalAnchor ? { contextEngineTerminalAnchor: terminalAnchor } : {},
4551
+ ...settledTurnFinalizationContext ? { settledTurnFinalizationContext } : {},
4552
+ ...resourceState.runtimeArtifact ? { runtimeArtifact: resourceState.runtimeArtifact } : {},
4553
+ ...resourceState.runtimeContinuationStarted ? { runtimeContinuationStarted: true } : {},
4554
+ ...!finalAborted && !effectiveTimedOut && !finalPromptError && preparedAuthBinding ? { authBindingFingerprint: preparedAuthBinding.fingerprint } : {},
4555
+ systemPromptReport
4556
+ });
4557
+ if (turnSucceeded && toolState.yieldDetected && !runAbortController.signal.aborted) resourceState.nativeHookRelay?.authorizeRetentionAfterSuccessfulYield();
4558
+ return finalizedResult;
4559
+ } finally {
4560
+ closeSettlement();
4143
4561
  }
4144
- recordCodexTrajectoryCompletion(trajectoryRecorder, {
4145
- attempt: params,
4146
- result,
4147
- threadId: resourceState.thread.threadId,
4148
- turnId: activeTurnId,
4149
- timedOut: effectiveTimedOut,
4150
- yieldDetected: toolState.yieldDetected
4151
- });
4152
- trajectoryRecorder?.recordEvent("session.ended", {
4153
- status: finalPromptError ? "error" : finalAborted || effectiveTimedOut ? "interrupted" : "success",
4154
- threadId: resourceState.thread.threadId,
4155
- turnId: activeTurnId,
4156
- timedOut: effectiveTimedOut,
4157
- yieldDetected: toolState.yieldDetected,
4158
- promptError: normalizeCodexTrajectoryError(finalPromptError)
4159
- });
4160
- markTrajectoryEndRecorded();
4161
- const terminalAssistantText = collectTerminalAssistantText(result);
4162
- if (terminalAssistantText && (!streamState.eventEmitted || streamState.needsTerminalSnapshot) && !finalAborted && !finalPromptError) emitCodexAppServerEvent(params, {
4163
- stream: "assistant",
4164
- data: { text: terminalAssistantText }
4165
- });
4166
- emitLifecycleTerminal(finalPromptError ? {
4167
- phase: "error",
4168
- error: formatErrorMessage(finalPromptError),
4169
- ...buildLifecycleTerminalMeta({
4170
- aborted: finalAborted,
4171
- timedOut: effectiveTimedOut
4172
- })
4173
- } : {
4174
- phase: "end",
4175
- ...buildLifecycleTerminalMeta({
4176
- aborted: finalAborted,
4177
- timedOut: effectiveTimedOut,
4178
- yielded: toolState.yieldDetected
4179
- })
4180
- });
4181
- const finalizedResult = Object.assign(result, {
4182
- ...toolState.yieldAcknowledgment ? { yieldAcknowledgment: toolState.yieldAcknowledgment } : {},
4183
- terminal: attemptTerminal.normalize({
4184
- timedOut: effectiveTimedOut,
4185
- aborted: finalAborted,
4186
- promptError: finalPromptError,
4187
- promptErrorSource: finalPromptErrorSource
4188
- }),
4189
- ...codexAppServerFailure ? { codexAppServerFailure } : {},
4190
- ...promptTimeoutOutcome ? { promptTimeoutOutcome } : {},
4191
- ...assistantTranscriptOwned ? { assistantTranscriptOwned: true } : {},
4192
- ...assistantTranscriptIdempotencyKey ? { assistantTranscriptIdempotencyKey } : {},
4193
- ...terminalAnchor ? { contextEngineTerminalAnchor: terminalAnchor } : {},
4194
- ...settledTurnFinalizationContext ? { settledTurnFinalizationContext } : {},
4195
- ...resourceState.runtimeArtifact ? { runtimeArtifact: resourceState.runtimeArtifact } : {},
4196
- ...resourceState.runtimeContinuationStarted ? { runtimeContinuationStarted: true } : {},
4197
- ...!finalAborted && !effectiveTimedOut && !finalPromptError && preparedAuthBinding ? { authBindingFingerprint: preparedAuthBinding.fingerprint } : {},
4198
- systemPromptReport
4199
- });
4200
- if (turnSucceeded && toolState.yieldDetected && !runAbortController.signal.aborted) resourceState.nativeHookRelay?.authorizeRetentionAfterSuccessfulYield();
4201
- return finalizedResult;
4202
4562
  }
4203
4563
  //#endregion
4204
4564
  //#region extensions/codex/src/app-server/attempt-notification-state.ts
@@ -4422,7 +4782,7 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
4422
4782
  const { attemptTools, runtime } = context;
4423
4783
  const { appServer, runAbortController } = runtime.connection;
4424
4784
  const { allocateCodexToolOutcomeOrdinal } = attemptTools;
4425
- const { state, turnIdRef, userInputBridgeRef, steeringQueueRef, activeTurnItemIds, pendingOpenClawDynamicToolCompletionIds, completeTurn, noteProgress, deadlines } = turnRuntime;
4785
+ const { state, turnIdRef, userInputBridgeRef, steeringQueueRef, activeTurnItemIds, pendingOpenClawDynamicToolCompletionIds, openClawDynamicToolExecutions, completeTurn, noteProgress, deadlines } = turnRuntime;
4426
4786
  const { scheduleTerminalDynamicToolReleaseCheck, reportExecutionNotification, maybeAnnounceFastModeAutoOff } = lifecycle;
4427
4787
  const isTerminalTurnNotificationForTurn = (notification, notificationTurnId) => isTerminalCodexTurnNotificationForTurn({
4428
4788
  notification,
@@ -4457,7 +4817,15 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
4457
4817
  if (notification.method === "item/completed") {
4458
4818
  if (activeTurnItemIds.size === 0) scheduleTerminalDynamicToolReleaseCheck();
4459
4819
  const item = readCodexNotificationItem(notification.params);
4460
- if (item?.type === "userMessage" && typeof item.clientId === "string") await steeringQueueRef.current?.confirmConsumed(item.clientId);
4820
+ if (item?.type === "dynamicToolCall" && typeof item.id === "string") {
4821
+ const response = await openClawDynamicToolExecutions.get({
4822
+ threadId: resourceState.thread.threadId,
4823
+ turnId,
4824
+ callId: item.id
4825
+ });
4826
+ if (response && !state.projectionClosed && !runAbortController.signal.aborted && turnIdRef.current === turnId && item.success === response.success && isDeepStrictEqual(item.contentItems, response.contentItems)) acknowledgeInternalToolResult(response);
4827
+ }
4828
+ if (item?.type === "userMessage" && typeof item.clientId === "string") steeringQueueRef.current?.confirmConsumed(item.clientId);
4461
4829
  }
4462
4830
  if (isCodexTurnAbortMarkerNotification(notification, { currentPromptTexts: [turnState.codexTurnPromptText] })) state.sawCodexInterruptMarker = true;
4463
4831
  }
@@ -4498,6 +4866,8 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
4498
4866
  if (!projector || !turnId || state.projectionClosed) return;
4499
4867
  projector.recordMcpToolCallReceipt(notification);
4500
4868
  if (isTerminalTurnNotificationForTurn(notification, turnId)) {
4869
+ const completed = readCodexTurnCompletedNotification(notification.params);
4870
+ if (completed) projector.settlement.terminalReceipt = completed.turn;
4501
4871
  state.terminalTurnNotificationQueued = true;
4502
4872
  steeringQueueRef.current?.sealAdmission();
4503
4873
  deadlines.beginSettlement(receivedAtMs);
@@ -4506,7 +4876,10 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
4506
4876
  const modelToolCallId = readRawResponseToolCallId(notification);
4507
4877
  if (modelToolCallId) allocateCodexToolOutcomeOrdinal?.(modelToolCallId);
4508
4878
  const nativeItem = readCodexNotificationItem(notification.params);
4509
- if (nativeItem?.type === "webSearch") projector.recordNativeToolOutcome(nativeItem);
4879
+ if (nativeItem?.type === "webSearch") {
4880
+ projector.settlement.turnTainted ||= notification.method === "item/completed";
4881
+ projector.recordNativeToolOutcome(nativeItem);
4882
+ }
4510
4883
  }
4511
4884
  };
4512
4885
  const enqueueNotification = async (notification, scope) => {
@@ -4533,17 +4906,47 @@ function isRestrictivePromptToolsAllow(toolsAllow) {
4533
4906
  return toolsAllow !== void 0 && !toolsAllow.some((name) => name.trim() === "*");
4534
4907
  }
4535
4908
  async function prepareCodexAttemptPrompt(context) {
4536
- const { runtime, attemptTools, historyState, hookContext, workspaceBootstrapContext, buildActiveContextEngineRuntimeContext, baseDeveloperInstructions, openClawPromptContext, skillsCollaborationInstructions, promptState, codexContextProjectionMaxChars, codexContinuityProjectionMaxChars } = context;
4909
+ const { runtime, attemptTools, historyState, hookContext, workspaceBootstrapContext, buildActiveContextEngineRuntimeContext, baseDeveloperInstructions, buildOpenClawPromptContext, skillsCollaborationInstructions, promptState, codexContextProjectionMaxChars, codexContinuityProjectionMaxChars } = context;
4537
4910
  const { connection, buildActiveRunAttemptParams, effectiveContextTokenBudget, effectiveRuntimeModelId, effectiveRuntimeProviderId } = runtime;
4538
4911
  const { params, activeContextEngine, usesSupervisionConnection, mutable, isInactiveThreadBootstrapBinding, bindingStore, bindingIdentity, agentDir, appServer, contextSessionKey, effectiveWorkspace, sandbox } = connection;
4539
4912
  const { toolBridge } = attemptTools;
4540
- const applyFreshThreadContinuityProjection = () => {
4541
- const projection = projectContextEngineAssemblyForCodex({
4542
- assembledMessages: historyState.messages,
4913
+ let contextImages = [];
4914
+ const currentUserTurnIdempotencyKey = params.userTurnTranscriptRecorder?.message?.idempotencyKey;
4915
+ const assertProjectionCurrent = () => {
4916
+ params.hostCapabilities.assertActive();
4917
+ connection.assertCurrent();
4918
+ connection.runAbortController.signal.throwIfAborted();
4919
+ };
4920
+ const prepareFileContext = async (message, maxChars) => {
4921
+ const prepare = params.hostCapabilities.prepareContextMedia;
4922
+ if (!prepare) {
4923
+ const media = asOptionalRecord(Reflect.get(message, "__openclaw"))?.media;
4924
+ if (Array.isArray(media) && media.length || message.role === "user" && Array.isArray(message.content) && message.content.some((part) => part.type === "image")) throw new CodexContextAttachmentError("Saved attachments require a newer OpenClaw host. Update the Gateway and Codex plugin together, then retry.");
4925
+ return { images: [] };
4926
+ }
4927
+ try {
4928
+ const files = await prepare({
4929
+ message,
4930
+ maxChars
4931
+ });
4932
+ assertProjectionCurrent();
4933
+ return files;
4934
+ } catch (error) {
4935
+ assertProjectionCurrent();
4936
+ throw new CodexContextAttachmentError("Saved attachment preparation failed. Retry the request after checking the attachment source.", { cause: error });
4937
+ }
4938
+ };
4939
+ const applyContinuityProjection = async (messages) => {
4940
+ const projection = await projectContextEngineAssemblyForCodex({
4941
+ assembledMessages: messages,
4543
4942
  originalHistoryMessages: historyState.messages,
4544
4943
  prompt: params.prompt,
4545
- maxRenderedContextChars: codexContinuityProjectionMaxChars
4944
+ maxRenderedContextChars: codexContinuityProjectionMaxChars,
4945
+ prepareFileContext,
4946
+ currentUserTurnIdempotencyKey
4546
4947
  });
4948
+ assertProjectionCurrent();
4949
+ contextImages = projection.images ?? [];
4547
4950
  promptState.promptText = projection.promptText;
4548
4951
  promptState.promptContextRange = projection.promptContextRange;
4549
4952
  promptState.prePromptMessageCount = projection.prePromptMessageCount;
@@ -4572,14 +4975,6 @@ async function prepareCodexAttemptPrompt(context) {
4572
4975
  });
4573
4976
  if (!assembled) throw new Error("context engine assemble returned no result");
4574
4977
  const contextEngineProjection = readContextEngineThreadBootstrapProjection(assembled.contextProjection);
4575
- const projection = projectContextEngineAssemblyForCodex({
4576
- assembledMessages: assembled.messages,
4577
- originalHistoryMessages: historyState.messages,
4578
- prompt: params.prompt,
4579
- systemPromptAddition: assembled.systemPromptAddition,
4580
- maxRenderedContextChars: codexContextProjectionMaxChars,
4581
- toolPayloadMode: contextEngineProjection ? "preserve" : "elide"
4582
- });
4583
4978
  const projectionDecision = contextEngineProjection ? resolveContextEngineBootstrapProjectionDecision({
4584
4979
  startupBinding: decisionStartupBinding,
4585
4980
  expectedBinding: buildContextEngineBinding(buildActiveRunAttemptParams(), contextEngineProjection),
@@ -4590,6 +4985,18 @@ async function prepareCodexAttemptPrompt(context) {
4590
4985
  project: true,
4591
4986
  reason: "per-turn-projection"
4592
4987
  };
4988
+ const projection = await projectContextEngineAssemblyForCodex({
4989
+ assembledMessages: assembled.messages,
4990
+ originalHistoryMessages: historyState.messages,
4991
+ prompt: params.prompt,
4992
+ systemPromptAddition: assembled.systemPromptAddition,
4993
+ maxRenderedContextChars: codexContextProjectionMaxChars,
4994
+ toolPayloadMode: contextEngineProjection ? "preserve" : "elide",
4995
+ ...projectionDecision.project ? { prepareFileContext } : {},
4996
+ currentUserTurnIdempotencyKey
4997
+ });
4998
+ assertProjectionCurrent();
4999
+ contextImages = projectionDecision.project ? projection.images ?? [] : [];
4593
5000
  const decisionBinding = decisionStartupBinding;
4594
5001
  embeddedAgentLog.info("codex app-server context-engine projection decision", {
4595
5002
  sessionId: params.sessionId,
@@ -4617,6 +5024,8 @@ async function prepareCodexAttemptPrompt(context) {
4617
5024
  if (activeContextEngine) try {
4618
5025
  await applyActiveContextEngineProjection(runtime.nativeToolSurfaceEnabled ? mutable.startupBinding : void 0);
4619
5026
  } catch (assembleErr) {
5027
+ if (assembleErr instanceof CodexContextAttachmentError) throw assembleErr;
5028
+ assertProjectionCurrent();
4620
5029
  embeddedAgentLog.warn("context engine assemble failed; using Codex baseline prompt", { error: formatErrorMessage(assembleErr) });
4621
5030
  }
4622
5031
  const codexModelInputHistoryMessages = [];
@@ -4633,7 +5042,7 @@ async function prepareCodexAttemptPrompt(context) {
4633
5042
  toolAuthority: {
4634
5043
  fingerprint: params.toolAuthorityFingerprint,
4635
5044
  activeToolNames: () => flattenCodexDynamicToolFunctions(toolBridge.availableSpecs).map((tool) => tool.name).filter(isNonEmptyString),
4636
- assertActive: params.hostCapabilities.assertActive
5045
+ assertActive: connection.assertCurrent
4637
5046
  }
4638
5047
  });
4639
5048
  };
@@ -4663,8 +5072,8 @@ async function prepareCodexAttemptPrompt(context) {
4663
5072
  }
4664
5073
  };
4665
5074
  };
4666
- const decorateCodexTurnPromptText = (promptBuildResult) => {
4667
- const turnPromptText = prependCodexOpenClawPromptContext(promptBuildResult.prompt, openClawPromptContext, { preservePromptWithoutContext: params.bootstrapContextMode === "lightweight" && params.bootstrapContextRunKind === "cron" });
5075
+ const decorateCodexTurnPromptText = (promptBuildResult, includeWorkspaceReferences = true) => {
5076
+ const turnPromptText = prependCodexOpenClawPromptContext(promptBuildResult.prompt, buildOpenClawPromptContext(includeWorkspaceReferences), { preservePromptWithoutContext: params.bootstrapContextMode === "lightweight" && params.bootstrapContextRunKind === "cron" });
4668
5077
  const projectedRanges = resolveShiftedPromptContextRange(promptBuildResult.prompt, promptBuildResult.promptInputRange, turnPromptText);
4669
5078
  const preservedRange = resolveShiftedPromptInputRange(promptBuildResult.prompt, promptBuildResult.promptInputRange, turnPromptText) ?? resolveCodexDeliveryHintPreservedInputRange({
4670
5079
  prompt: promptBuildResult.prompt,
@@ -4704,28 +5113,19 @@ async function prepareCodexAttemptPrompt(context) {
4704
5113
  const selectNewerVisibleHistoryAfterBinding = (binding) => {
4705
5114
  const cutoff = Date.parse(binding.historyCoveredThrough ?? "");
4706
5115
  return historyState.messages.filter((message) => {
4707
- if (message.role !== "user" && message.role !== "assistant") return false;
5116
+ if (message.role !== "user" && message.role !== "assistant" && !isCodexDurableCustomMessage(message)) return false;
4708
5117
  const mirrorIdentity = readMirrorIdentity(message);
4709
5118
  const timestamp = typeof message.timestamp === "number" ? message.timestamp : typeof message.timestamp === "string" ? Date.parse(message.timestamp) : NaN;
4710
5119
  return !("idempotencyKey" in message && typeof message.idempotencyKey === "string" && message.idempotencyKey.startsWith("codex-app-server:")) && !hasCodexMirrorOrigin(message) && !mirrorIdentity?.startsWith("codex-app-server:") && Number.isFinite(timestamp) && timestamp > (Number.isFinite(cutoff) ? cutoff : 0);
4711
5120
  });
4712
5121
  };
4713
- const applyResumeStaleBindingContinuityProjection = (binding) => {
5122
+ const applyResumeStaleBindingContinuityProjection = async (binding) => {
4714
5123
  const newerVisibleMessages = selectNewerVisibleHistoryAfterBinding(binding);
4715
5124
  if (newerVisibleMessages.length === 0) return false;
4716
- const projection = projectContextEngineAssemblyForCodex({
4717
- assembledMessages: newerVisibleMessages,
4718
- originalHistoryMessages: historyState.messages,
4719
- prompt: params.prompt,
4720
- maxRenderedContextChars: codexContinuityProjectionMaxChars
4721
- });
4722
- promptState.promptText = projection.promptText;
4723
- promptState.promptContextRange = projection.promptContextRange;
4724
- promptState.prePromptMessageCount = projection.prePromptMessageCount;
4725
- promptState.noEngineContinuityProjectionApplied = true;
5125
+ await applyContinuityProjection(newerVisibleMessages);
4726
5126
  return true;
4727
5127
  };
4728
- const precomputeNoContextEngineStaleBindingProjection = () => {
5128
+ const precomputeNoContextEngineStaleBindingProjection = async () => {
4729
5129
  promptState.precomputedStaleBindingContinuityProjectionApplied = false;
4730
5130
  promptState.staleBindingContinuityForcedFreshStart = false;
4731
5131
  const binding = mutable.startupBinding;
@@ -4734,30 +5134,31 @@ async function prepareCodexAttemptPrompt(context) {
4734
5134
  promptState.inactiveThreadBootstrapBindingForcedFreshStart = true;
4735
5135
  return false;
4736
5136
  }
4737
- const projected = applyResumeStaleBindingContinuityProjection(binding);
5137
+ const projected = await applyResumeStaleBindingContinuityProjection(binding);
4738
5138
  promptState.precomputedStaleBindingContinuityProjectionApplied = projected;
4739
5139
  return projected;
4740
5140
  };
4741
- const applyNoContextEngineContinuityProjection = (action, binding) => {
4742
- const hasContinuity = historyState.messages.some((message) => message.role === "user" || action === "started" && (message.role === "compactionSummary" || message.role === "branchSummary"));
5141
+ const applyNoContextEngineContinuityProjection = async (action, binding) => {
5142
+ const hasContinuity = historyState.messages.some((message) => message.role === "user" || isCodexDurableCustomMessage(message) || action === "started" && (message.role === "compactionSummary" || message.role === "branchSummary"));
4743
5143
  if (activeContextEngine || !hasContinuity) return false;
4744
5144
  if (action === "resumed" && promptState.precomputedStaleBindingContinuityProjectionApplied) return true;
4745
5145
  if (action === "started" && promptState.staleBindingContinuityForcedFreshStart) return true;
4746
5146
  if (action === "started" && promptState.inactiveThreadBootstrapBindingForcedFreshStart) return false;
4747
5147
  if (action === "resumed" && binding) return applyResumeStaleBindingContinuityProjection(binding);
4748
5148
  if (action === "started") {
4749
- applyFreshThreadContinuityProjection();
5149
+ await applyContinuityProjection(historyState.messages);
4750
5150
  return true;
4751
5151
  }
4752
5152
  return false;
4753
5153
  };
4754
- if (precomputeNoContextEngineStaleBindingProjection()) await rebuildCodexPromptBuildFromCurrentProjection();
5154
+ if (await precomputeNoContextEngineStaleBindingProjection()) await rebuildCodexPromptBuildFromCurrentProjection();
4755
5155
  const rotateStartupBindingForProjectedTurn = async () => {
4756
5156
  const binding = mutable.startupBinding;
4757
5157
  if (!binding?.threadId) return;
4758
5158
  const previousThreadId = binding.threadId;
4759
5159
  const hadInactiveThreadBootstrapBinding = isInactiveThreadBootstrapBinding(binding);
4760
5160
  const startupBindingResolution = await rotateOversizedCodexAppServerStartupBinding({
5161
+ assertCurrent: connection.assertCurrent,
4761
5162
  binding,
4762
5163
  bindingStore,
4763
5164
  identity: bindingIdentity,
@@ -4766,6 +5167,7 @@ async function prepareCodexAttemptPrompt(context) {
4766
5167
  codexHome: appServer.start.env?.CODEX_HOME,
4767
5168
  config: params.config,
4768
5169
  contextEngineActive: Boolean(activeContextEngine),
5170
+ expectedSessionRuntimeOwnership: params.expectedSessionRuntimeOwnership,
4769
5171
  projectedTurnTokens: estimateCodexAppServerProjectedTurnTokens({
4770
5172
  prompt: turnState.codexTurnPromptText,
4771
5173
  developerInstructions: buildRenderedCodexDeveloperInstructions()
@@ -4776,12 +5178,14 @@ async function prepareCodexAttemptPrompt(context) {
4776
5178
  if (mutable.startupBinding?.threadId) return;
4777
5179
  promptState.inactiveThreadBootstrapBindingForcedFreshStart = hadInactiveThreadBootstrapBinding;
4778
5180
  promptState.staleBindingContinuityForcedFreshStart = promptState.precomputedStaleBindingContinuityProjectionApplied && !promptState.inactiveThreadBootstrapBindingForcedFreshStart;
4779
- if (promptState.staleBindingContinuityForcedFreshStart) applyFreshThreadContinuityProjection();
5181
+ if (promptState.staleBindingContinuityForcedFreshStart) await applyContinuityProjection(historyState.messages);
4780
5182
  if (activeContextEngine) {
4781
5183
  promptState.contextEngineProjection = void 0;
4782
5184
  try {
4783
5185
  await applyActiveContextEngineProjection(void 0);
4784
5186
  } catch (assembleErr) {
5187
+ if (assembleErr instanceof CodexContextAttachmentError) throw assembleErr;
5188
+ assertProjectionCurrent();
4785
5189
  embeddedAgentLog.warn("context engine assemble failed; using Codex baseline prompt", { error: formatErrorMessage(assembleErr) });
4786
5190
  }
4787
5191
  }
@@ -4795,22 +5199,36 @@ async function prepareCodexAttemptPrompt(context) {
4795
5199
  });
4796
5200
  };
4797
5201
  await rotateStartupBindingForProjectedTurn();
5202
+ const buildSystemPromptReport = (omitWorkspaceReferences = false) => buildCodexSystemPromptReport({
5203
+ attempt: params,
5204
+ sessionKey: contextSessionKey,
5205
+ workspaceDir: effectiveWorkspace,
5206
+ developerInstructions: buildRenderedCodexDeveloperInstructions(),
5207
+ workspaceBootstrapContext,
5208
+ omitWorkspaceReferences,
5209
+ skillsPrompt: skillsCollaborationInstructions ? params.skillsSnapshot?.prompt ?? "" : "",
5210
+ tools: toolBridge.availableSpecs
5211
+ });
5212
+ const systemPromptReport = buildSystemPromptReport();
5213
+ let workspaceReferencesIncluded = true;
4798
5214
  return {
4799
5215
  context,
5216
+ get contextImages() {
5217
+ return contextImages;
5218
+ },
4800
5219
  codexModelInputHistoryMessages,
4801
5220
  turnState,
5221
+ refreshWorkspaceReferences: (include) => {
5222
+ turnState.codexTurnPromptText = decorateCodexTurnPromptText(turnState.promptBuild, include);
5223
+ if (include !== workspaceReferencesIncluded) {
5224
+ Object.assign(systemPromptReport, buildSystemPromptReport(!include));
5225
+ workspaceReferencesIncluded = include;
5226
+ }
5227
+ },
4802
5228
  buildRenderedCodexDeveloperInstructions,
4803
5229
  rebuildCodexTurnPromptTextFromCurrentProjection,
4804
5230
  applyNoContextEngineContinuityProjection,
4805
- systemPromptReport: buildCodexSystemPromptReport({
4806
- attempt: params,
4807
- sessionKey: contextSessionKey,
4808
- workspaceDir: effectiveWorkspace,
4809
- developerInstructions: buildRenderedCodexDeveloperInstructions(),
4810
- workspaceBootstrapContext,
4811
- skillsPrompt: skillsCollaborationInstructions ? params.skillsSnapshot?.prompt ?? "" : "",
4812
- tools: toolBridge.availableSpecs
4813
- })
5231
+ systemPromptReport
4814
5232
  };
4815
5233
  }
4816
5234
  //#endregion
@@ -4909,10 +5327,11 @@ function prepareCodexAttemptResources(prompt) {
4909
5327
  closed: retired.closed,
4910
5328
  matchedSharedClient: retired.found
4911
5329
  });
4912
- if (retired.closed) await state.client.closeAndWait({
5330
+ const result = retired.closed ? await state.client.closeAndWait({
4913
5331
  exitTimeoutMs: 2e3,
4914
5332
  forceKillDelayMs: 250
4915
- });
5333
+ }) : void 0;
5334
+ if (params.oneShotCliRun && result?.cleanup !== "closed") throw new Error("Codex one-shot client cleanup could not be confirmed");
4916
5335
  };
4917
5336
  const releaseSandboxExecEnvironment = async () => {
4918
5337
  if (state.sandboxExecEnvironment) {
@@ -4926,10 +5345,13 @@ function prepareCodexAttemptResources(prompt) {
4926
5345
  await releaseSandboxExecEnvironment();
4927
5346
  const ownedClient = state.releaseSharedClientLease ? state.client : void 0;
4928
5347
  releaseSharedClientLeaseOnce();
4929
- await ownedClient?.closeAndWait({
4930
- exitTimeoutMs: 2e3,
4931
- forceKillDelayMs: 250
4932
- });
5348
+ if (ownedClient) {
5349
+ const result = await ownedClient.closeAndWait({
5350
+ exitTimeoutMs: 2e3,
5351
+ forceKillDelayMs: 250
5352
+ });
5353
+ if (params.oneShotCliRun && result.cleanup !== "closed") throw new Error("Codex isolated client cleanup could not be confirmed");
5354
+ }
4933
5355
  return;
4934
5356
  }
4935
5357
  releaseSharedClientLeaseOnce();
@@ -5016,6 +5438,7 @@ function prepareCodexAttemptResources(prompt) {
5016
5438
  loopDetectionPreToolUseRelay: appServer.loopDetectionPreToolUseRelay,
5017
5439
  signal: runAbortController.signal,
5018
5440
  hostCapabilities: params.hostCapabilities,
5441
+ assertCurrent: connection.assertCurrent,
5019
5442
  onPreToolUseFailure: (failure) => {
5020
5443
  const projector = projectorRef.current;
5021
5444
  if (projector) projector.recordNativeToolPreToolUseFailure(failure);
@@ -5057,7 +5480,7 @@ function prepareCodexAttemptResources(prompt) {
5057
5480
  async function prepareCodexAttemptRoute(resources, turnRuntime, notifications, handleServerRequest) {
5058
5481
  const { prompt, state: resourceState, trajectoryRecorder, releaseCurrentRoute, registerNativeSubagentMonitor, activateNativePreToolUseFailureFallback, releaseSandboxExecEnvironment, releaseSharedClientLeaseOnce } = resources;
5059
5482
  const { connection } = prompt.context.runtime;
5060
- const { params, runAbortController, abortFromUpstream } = connection;
5483
+ const { runAbortController } = connection;
5061
5484
  const { state, turnIdRef, completeTurn } = turnRuntime;
5062
5485
  const { noteNotificationReceived, enqueueNotification } = notifications;
5063
5486
  const attachRouteAbort = (route) => {
@@ -5112,7 +5535,6 @@ async function prepareCodexAttemptRoute(resources, turnRuntime, notifications, h
5112
5535
  resourceState.nativeHookRelay?.unregister();
5113
5536
  await releaseSandboxExecEnvironment();
5114
5537
  releaseSharedClientLeaseOnce();
5115
- params.abortSignal?.removeEventListener("abort", abortFromUpstream);
5116
5538
  throw error;
5117
5539
  }
5118
5540
  return { ensureCurrentThreadRoute };
@@ -5125,6 +5547,7 @@ function prewarmCodexAttemptClient(params) {
5125
5547
  const { appServer, attemptClientFactory, options, pluginConfig, runtimeArtifactRequest, startupAuthRequirement, startupClientAuthProfileId, startupPreparedAuth, agentDir, params: runParams, runAbortController } = connection;
5126
5548
  if (options.clientFactory || attemptClientFactory !== getLeasedSharedCodexAppServerClient || runtimeArtifactRequest) return;
5127
5549
  getSharedCodexAppServerClient({
5550
+ assertCurrent: connection.assertCurrent,
5128
5551
  startOptions: appServer.start,
5129
5552
  pluginConfig,
5130
5553
  ...startupPreparedAuth ? { preparedAuth: startupPreparedAuth } : { authProfileId: startupClientAuthProfileId },
@@ -5236,7 +5659,7 @@ async function prepareCodexAttemptRuntime(connection) {
5236
5659
  const authenticatedScheduledMode = params.trigger === "cron" && params.scheduledToolPolicy !== void 0 && Array.isArray(params.toolsAllow);
5237
5660
  const scheduledConfiguredMcpSurface = authenticatedScheduledMode && (bundleMcpThreadConfig.staticServerNames.length > 0 || mutable.startupBinding?.configuredMcpOwnershipVersion === 1);
5238
5661
  const cronCreatorAuthorityCapability = params.cronCreatorAuthorityCapability;
5239
- const hasFreshCreatorAuthority = cronCreatorAuthorityCapability?.active === true && cronCreatorAuthorityCapability.runId === params.runId && !cronCreatorAuthorityCapability.signal.aborted;
5662
+ const hasFreshCreatorAuthority = cronCreatorAuthorityCapability?.active === true && !(cronCreatorAuthorityCapability.controlUiAdmin && cronCreatorAuthorityCapability.callerOrigin.kind === "unknown") && cronCreatorAuthorityCapability.runId === params.runId && !cronCreatorAuthorityCapability.signal.aborted;
5240
5663
  const mayResolveScheduledConfiguredMcpCreatorAuthority = !authenticatedScheduledMode && canResolveScheduledConfiguredMcpCreatorAuthority({
5241
5664
  trigger: params.trigger,
5242
5665
  connectionClass: appServer.connectionClass,
@@ -5401,10 +5824,10 @@ function createCodexDynamicToolExecutionRegistry() {
5401
5824
  }
5402
5825
  };
5403
5826
  }
5404
- function resolveCodexDynamicToolDirectNames(params, hostSystemAgentActive = false) {
5827
+ function resolveCodexDynamicToolDirectNames(params, registeredTools, hostSystemAgentActive = false) {
5405
5828
  const names = [];
5406
5829
  if (hostSystemAgentActive && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow)) names.push("openclaw");
5407
- if (params.sourceReplyDeliveryMode === "message_tool_only") names.push("message");
5830
+ if (registeredTools.some((tool) => tool.name === "message")) names.push("message");
5408
5831
  return names;
5409
5832
  }
5410
5833
  //#endregion
@@ -5677,7 +6100,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
5677
6100
  //#region extensions/codex/src/app-server/attempt-runtime-artifact.ts
5678
6101
  async function verifyStartupArtifact(params) {
5679
6102
  if (!params.request) return;
5680
- const { readCodexAppServerClientRuntimeArtifact, validateCodexAppServerRuntimeArtifact } = await import("./runtime-artifact-C3jVimFn.js");
6103
+ const { readCodexAppServerClientRuntimeArtifact, validateCodexAppServerRuntimeArtifact } = await import("./runtime-artifact-T-NwqeSH.js");
5681
6104
  const runtimeArtifact = readCodexAppServerClientRuntimeArtifact(params.client);
5682
6105
  const expected = params.request.expected;
5683
6106
  const matchesExpected = !expected || runtimeArtifact?.id === expected.id && runtimeArtifact.fingerprint === expected.fingerprint;
@@ -5689,12 +6112,7 @@ async function verifyStartupArtifact(params) {
5689
6112
  }
5690
6113
  //#endregion
5691
6114
  //#region extensions/codex/src/app-server/computer-use-health.ts
5692
- const COMPUTER_USE_HEALTH_MONITOR_STATE = Symbol.for("openclaw.codexComputerUseHealthMonitorState");
5693
- function getComputerUseHealthMonitorState() {
5694
- const globalState = globalThis;
5695
- globalState[COMPUTER_USE_HEALTH_MONITOR_STATE] ??= { monitors: /* @__PURE__ */ new WeakMap() };
5696
- return globalState[COMPUTER_USE_HEALTH_MONITOR_STATE];
5697
- }
6115
+ const getComputerUseHealthMonitorState = defineCodexBuildState("openclaw.codexComputerUseHealthMonitorState", () => ({ monitors: /* @__PURE__ */ new WeakMap() }));
5698
6116
  function startCodexComputerUseHealthMonitor(params) {
5699
6117
  const state = getComputerUseHealthMonitorState();
5700
6118
  const existing = state.monitors.get(params.client);
@@ -5802,15 +6220,22 @@ function resolveCodexPluginThreadConfigStartupPolicy(params) {
5802
6220
  /** Builds plugin config without allowing sequential RPC timeouts to consume the turn. */
5803
6221
  async function buildCodexPluginThreadConfigWithinDeadline(params) {
5804
6222
  const { requestTimeoutMs, signal, request, failClosedOnTimeout, transform, ...buildParams } = params;
6223
+ signal.throwIfAborted();
5805
6224
  const timeoutMs = resolveCodexPluginThreadConfigTimeoutMs(requestTimeoutMs);
5806
6225
  const deadlineMs = Date.now() + timeoutMs;
5807
- const boundedRequest = (method, requestParams) => {
6226
+ let requestTimedOut = false;
6227
+ const boundedRequest = async (method, requestParams) => {
5808
6228
  const remainingTimeoutMs = deadlineMs - Date.now();
5809
- if (remainingTimeoutMs <= 0) throw new CodexPluginThreadConfigDeadlineError();
5810
- return request(method, requestParams, {
5811
- timeoutMs: remainingTimeoutMs,
5812
- signal
5813
- });
6229
+ if (requestTimedOut || remainingTimeoutMs <= 0) throw new CodexPluginThreadConfigDeadlineError();
6230
+ try {
6231
+ return await request(method, requestParams, {
6232
+ timeoutMs: remainingTimeoutMs,
6233
+ signal
6234
+ });
6235
+ } catch (error) {
6236
+ requestTimedOut ||= isCodexPluginThreadConfigTimeoutError(error);
6237
+ throw error;
6238
+ }
5814
6239
  };
5815
6240
  try {
5816
6241
  return await withAbortableTimeout({
@@ -5821,14 +6246,16 @@ async function buildCodexPluginThreadConfigWithinDeadline(params) {
5821
6246
  ...buildParams,
5822
6247
  request: boundedRequest
5823
6248
  });
5824
- return transform ? await transform(config, boundedRequest) : config;
6249
+ const result = transform ? await transform(config, boundedRequest) : config;
6250
+ if (requestTimedOut || Date.now() >= deadlineMs) throw new CodexPluginThreadConfigDeadlineError();
6251
+ return result;
5825
6252
  })(),
5826
6253
  timeoutMessage: "Codex plugin thread config deadline elapsed",
5827
6254
  createTimeoutError: () => new CodexPluginThreadConfigDeadlineError()
5828
6255
  });
5829
6256
  } catch (error) {
5830
- if (signal.aborted || !isCodexPluginThreadConfigTimeoutError(error)) throw error;
5831
- if (failClosedOnTimeout) throw new AgentHarnessPreflightError(`Scheduled Codex app policy verification exceeded its ${timeoutMs} ms startup budget. No app tools were executed. Retry after Codex app inventory is responsive, or reauthorize the automation.`);
6257
+ if (signal.aborted || !requestTimedOut && !isCodexPluginThreadConfigTimeoutError(error) && Date.now() < deadlineMs) throw error;
6258
+ if (failClosedOnTimeout) throw new AgentHarnessPreflightError(`Codex app policy verification exceeded its ${timeoutMs} ms startup budget. No app tools were executed. Retry after Codex app inventory is responsive.`);
5832
6259
  return buildCodexPluginThreadConfigTimeoutFallback({
5833
6260
  pluginConfig: buildParams.pluginConfig,
5834
6261
  appCacheKey: buildParams.appCacheKey,
@@ -5842,7 +6269,7 @@ function createCodexPluginThreadConfigStartupProvider(params) {
5842
6269
  const metadataCache = configuredMetadataCache ?? defaultCodexPluginMetadataCache;
5843
6270
  return {
5844
6271
  enabled: true,
5845
- requiresCurrentPolicyCheck: Boolean(params.scheduledRuntimeAuthority),
6272
+ requiresCurrentPolicyCheck: Boolean(policy?.enabled || params.scheduledRuntimeAuthority),
5846
6273
  inputFingerprint,
5847
6274
  enabledPluginConfigKeys,
5848
6275
  accountAppRecoveryEnabled: policy?.allowAllPlugins,
@@ -5905,7 +6332,6 @@ async function startCodexAttemptThread(params) {
5905
6332
  let releaseSharedClientLease;
5906
6333
  let startupClientForAbandonedRequestCleanup;
5907
6334
  let releaseStartupResourcesOnTimeout;
5908
- let startupAbandoned = false;
5909
6335
  const startupAbandonController = new AbortController();
5910
6336
  const abandonStartupAcquire = () => startupAbandonController.abort();
5911
6337
  params.signal.addEventListener("abort", abandonStartupAcquire, { once: true });
@@ -5914,7 +6340,6 @@ async function startCodexAttemptThread(params) {
5914
6340
  timeoutMs: params.startupTimeoutMs,
5915
6341
  signal: params.signal,
5916
6342
  onTimeout: async () => {
5917
- startupAbandoned = true;
5918
6343
  startupAbandonController.abort();
5919
6344
  await params.onStartupTimeout();
5920
6345
  await releaseStartupResourcesOnTimeout?.();
@@ -5942,7 +6367,9 @@ async function startCodexAttemptThread(params) {
5942
6367
  let startupAttemptSucceeded = false;
5943
6368
  try {
5944
6369
  const attemptParams = params.buildAttemptParams();
6370
+ params.assertCurrent?.();
5945
6371
  startupClient = await params.attemptClientFactory({
6372
+ assertCurrent: params.assertCurrent,
5946
6373
  startOptions: params.appServer.start,
5947
6374
  pluginConfig: params.pluginConfig,
5948
6375
  ...params.startupPreparedAuth ? { preparedAuth: params.startupPreparedAuth } : { authProfileId: params.startupAuthProfileId },
@@ -5958,7 +6385,7 @@ async function startCodexAttemptThread(params) {
5958
6385
  config: params.config,
5959
6386
  onStartedClient: (client) => {
5960
6387
  startupClientForAbandonedRequestCleanup = client;
5961
- if (startupAbandoned || startupAbandonController.signal.aborted) closeCodexStartupClientBestEffort(client);
6388
+ if (startupAbandonController.signal.aborted) closeCodexStartupClientBestEffort(client);
5962
6389
  },
5963
6390
  abandonSignal: startupAbandonController.signal,
5964
6391
  timeoutMs: params.appServer.requestTimeoutMs
@@ -5974,13 +6401,13 @@ async function startCodexAttemptThread(params) {
5974
6401
  releaseSharedClientLease = startupClientLease;
5975
6402
  attemptedClient = activeStartupClient;
5976
6403
  startupClientForAbandonedRequestCleanup = activeStartupClient;
5977
- if (startupAbandoned) throw new CodexAppServerStartupError("timed_out");
5978
6404
  if (startupAbandonController.signal.aborted) throw new CodexAppServerStartupError("aborted");
5979
6405
  const runtimeArtifact = await verifyStartupArtifact({
5980
6406
  client: activeStartupClient,
5981
6407
  request: params.runtimeArtifactRequest,
5982
6408
  signal: startupAbandonController.signal
5983
6409
  });
6410
+ params.assertCurrent?.();
5984
6411
  ensureCodexAppServerClientRuntime(activeStartupClient, {
5985
6412
  agentDir: params.agentDir,
5986
6413
  authProfileId: startupRuntimeAuthProfileId,
@@ -6046,6 +6473,7 @@ async function startCodexAttemptThread(params) {
6046
6473
  };
6047
6474
  releaseStartupResourcesOnTimeout = releaseStartupSandboxEnvironment;
6048
6475
  try {
6476
+ params.assertCurrent?.();
6049
6477
  const sandboxEnvironmentRequired = shouldRequireCodexSandboxExecServerEnvironment({
6050
6478
  sandbox: params.sandbox,
6051
6479
  nativeToolSurfaceEnabled: params.nativeToolSurfaceEnabled,
@@ -6061,10 +6489,7 @@ async function startCodexAttemptThread(params) {
6061
6489
  onExecutionDisconnect: params.onExecutionDisconnect
6062
6490
  }) : void 0;
6063
6491
  startupSandboxEnvironmentAcquired = Boolean(startupSandboxEnvironment);
6064
- if (startupAbandonController.signal.aborted) {
6065
- await releaseStartupSandboxEnvironment();
6066
- throw new CodexAppServerStartupError("aborted");
6067
- }
6492
+ if (startupAbandonController.signal.aborted) throw new CodexAppServerStartupError("aborted");
6068
6493
  if (sandboxEnvironmentRequired && !startupSandboxEnvironment) throw new Error("Codex app-server did not register an OpenClaw sandbox exec-server environment.");
6069
6494
  } catch (error) {
6070
6495
  await releaseStartupSandboxEnvironment();
@@ -6101,6 +6526,7 @@ async function startCodexAttemptThread(params) {
6101
6526
  client: activeStartupClient,
6102
6527
  reserveResumeThread,
6103
6528
  bindingStore: params.bindingStore,
6529
+ assertCurrent: params.assertCurrent,
6104
6530
  params: params.buildAttemptParams(),
6105
6531
  runtimeModelId: params.runtimeModelId,
6106
6532
  agentId: params.sessionAgentId,
@@ -6191,7 +6617,7 @@ async function startCodexAttemptThread(params) {
6191
6617
  }
6192
6618
  } catch (error) {
6193
6619
  startupAttemptError = error;
6194
- if (!startupAbandoned && !params.signal.aborted && !startupClient) {
6620
+ if (!startupAbandonController.signal.aborted && !startupClient) {
6195
6621
  const sharedClient = clearSharedCodexAppServerClientIfCurrentAndUnclaimed(startupClientForAbandonedRequestCleanup);
6196
6622
  if (sharedClient.found && !sharedClient.closed) startupClientForAbandonedRequestCleanup = void 0;
6197
6623
  }
@@ -6200,10 +6626,7 @@ async function startCodexAttemptThread(params) {
6200
6626
  if (!startupAttemptSucceeded) {
6201
6627
  if (releaseSharedClientLease === startupClientLease) releaseSharedClientLease = void 0;
6202
6628
  startupClientLease?.();
6203
- if (startupAbandoned || params.signal.aborted) {
6204
- if (startupClientForAbandonedRequestCleanup === startupClient) startupClientForAbandonedRequestCleanup = void 0;
6205
- await closeCodexStartupClientBestEffort(startupClient);
6206
- } else if (!isCodexAppServerStartSelectionChangedError(startupAttemptError) && (shouldClearSharedClientAfterStartupRace(startupAttemptError) || shouldClearSharedClientAfterStartupFailure(startupAttemptError, params.spawnedBy))) {
6629
+ if (shouldRetireCodexStartupClient(startupAttemptError, params.spawnedBy, startupAbandonController.signal)) {
6207
6630
  if (startupClientForAbandonedRequestCleanup === startupClient) startupClientForAbandonedRequestCleanup = void 0;
6208
6631
  await closeCodexStartupClientBestEffort(startupClient);
6209
6632
  }
@@ -6214,7 +6637,7 @@ async function startCodexAttemptThread(params) {
6214
6637
  return await startupAttempt();
6215
6638
  } catch (error) {
6216
6639
  const selectionChanged = isCodexAppServerStartSelectionChangedError(error);
6217
- if (startupAbandoned || params.signal.aborted || !selectionChanged && !isCodexAppServerConnectionClosedError(error)) throw error;
6640
+ if (startupAbandonController.signal.aborted || !selectionChanged && !isCodexAppServerConnectionClosedError(error)) throw error;
6218
6641
  const refreshedSharedClient = selectionChanged ? retireSharedCodexAppServerClientIfCurrent(attemptedClient) : clearSharedCodexAppServerClientIfCurrent(attemptedClient);
6219
6642
  if (startupClientForAbandonedRequestCleanup === attemptedClient) startupClientForAbandonedRequestCleanup = void 0;
6220
6643
  if (attempt >= CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS) {
@@ -6247,12 +6670,7 @@ async function startCodexAttemptThread(params) {
6247
6670
  releaseSharedClientLease
6248
6671
  };
6249
6672
  } catch (error) {
6250
- if (params.signal.aborted || isCodexAppServerStartupError(error)) {
6251
- releaseSharedClientLease?.();
6252
- releaseSharedClientLease = void 0;
6253
- await closeCodexStartupClientBestEffort(startupClientForAbandonedRequestCleanup);
6254
- startupClientForAbandonedRequestCleanup = void 0;
6255
- } else if (!isCodexAppServerStartSelectionChangedError(error) && (shouldClearSharedClientAfterStartupRace(error) || shouldClearSharedClientAfterStartupFailure(error, params.spawnedBy))) {
6673
+ if (shouldRetireCodexStartupClient(error, params.spawnedBy, startupAbandonController.signal)) {
6256
6674
  releaseSharedClientLease?.();
6257
6675
  releaseSharedClientLease = void 0;
6258
6676
  await closeCodexStartupClientBestEffort(startupClientForAbandonedRequestCleanup);
@@ -6263,30 +6681,29 @@ async function startCodexAttemptThread(params) {
6263
6681
  params.signal.removeEventListener("abort", abandonStartupAcquire);
6264
6682
  }
6265
6683
  }
6266
- function shouldClearSharedClientAfterStartupRace(error) {
6267
- return isCodexAppServerStartupError(error) || isCodexAppServerRequestTimeoutError(error);
6268
- }
6269
- function shouldClearSharedClientAfterStartupFailure(error, spawnedBy) {
6270
- return !isCodexAppServerOverloadError(error) && !(error instanceof AgentHarnessPreflightError && error.scope === void 0) && (isCodexAppServerBrokenPipeError(error) || !spawnedBy);
6684
+ function shouldRetireCodexStartupClient(error, spawnedBy, signal) {
6685
+ if (signal.aborted || isCodexAppServerStartupError(error) || isCodexAppServerRequestTimeoutError(error)) return true;
6686
+ return !isCodexAppServerStartSelectionChangedError(error) && !isCodexAppServerOverloadError(error) && !(error instanceof AgentHarnessPreflightError && error.scope === void 0) && (isCodexAppServerBrokenPipeError(error) || !spawnedBy);
6271
6687
  }
6272
6688
  //#endregion
6273
6689
  //#region extensions/codex/src/app-server/run-attempt-start.ts
6274
6690
  async function startCodexAttemptRuntime(resources) {
6275
6691
  const { prompt, state, trajectoryRecorder, activateNativePreToolUseFailureFallback, releaseSandboxExecEnvironment, releaseSharedClientLeaseAndRetireOneShotClient, releaseCurrentRoute, runCleanupStep, startupTimeoutMs, buildNativeHookRelayFinalConfigPatch } = resources;
6276
- const { context, turnState, buildRenderedCodexDeveloperInstructions, rebuildCodexTurnPromptTextFromCurrentProjection, applyNoContextEngineContinuityProjection } = prompt;
6692
+ const { context, turnState, rebuildCodexTurnPromptTextFromCurrentProjection, applyNoContextEngineContinuityProjection } = prompt;
6277
6693
  const { runtime, attemptTools, promptState } = context;
6278
6694
  const { connection, runtimeParams, preparedAuthBinding, buildActiveRunAttemptParams, startupAuthAccountCacheKey, startupEnvApiKeyCacheKey, bundleMcpThreadConfig, nativeToolSurfaceEnabled, nativeProviderWebSearchSupport, effectiveRuntimeModelId, sandboxExecServerEnabled } = runtime;
6279
6695
  const { toolBridge, toolState } = attemptTools;
6280
6696
  const developerInstructions = joinPresentSections(turnState.promptBuild.developerInstructions, attemptTools.configuredMcp?.diagnosticNotice);
6281
- const { params, attemptClientFactory, bindingStore, appServer, pluginConfig, computerUseConfig, startupClientAuthProfileId, runtimeArtifactRequest, startupPreparedAuth, agentDir, sessionAgentId, effectiveWorkspace, effectiveCwd, sandbox, runAbortController, usesSupervisionConnection, resolveReviewerPolicyContext, resolveRuntimeOptionsForCurrentBinding, startupAuthProfileId, startupAuthRequirement, abortFromUpstream } = connection;
6697
+ const { params, attemptClientFactory, bindingStore, appServer, pluginConfig, computerUseConfig, startupClientAuthProfileId, runtimeArtifactRequest, startupPreparedAuth, agentDir, sessionAgentId, effectiveWorkspace, effectiveCwd, sandbox, runAbortController, usesSupervisionConnection, resolveReviewerPolicyContext, resolveRuntimeOptionsForCurrentBinding, startupAuthProfileId, startupAuthRequirement } = connection;
6282
6698
  let pluginAppServer = withCodexAppServerFastModeServiceTier(appServer, runtimeParams);
6283
- const loopDetectionEnabled = (sessionAgentId && params.config ? resolveAgentConfig$1(params.config, sessionAgentId)?.tools?.loopDetection?.enabled : void 0) ?? params.config?.tools?.loopDetection?.enabled ?? false;
6699
+ const loopDetectionEnabled = (sessionAgentId && params.config ? resolveAgentConfig(params.config, sessionAgentId)?.tools?.loopDetection?.enabled : void 0) ?? params.config?.tools?.loopDetection?.enabled ?? false;
6284
6700
  try {
6285
6701
  emitCodexAppServerEvent(params, {
6286
6702
  stream: "codex_app_server.lifecycle",
6287
6703
  data: { phase: "startup" }
6288
6704
  });
6289
6705
  const startupResult = await startCodexAttemptThread({
6706
+ assertCurrent: connection.assertCurrent,
6290
6707
  attemptClientFactory,
6291
6708
  bindingStore,
6292
6709
  runtime: connection.options.runtime,
@@ -6342,6 +6759,7 @@ async function startCodexAttemptRuntime(resources) {
6342
6759
  state.sandboxExecEnvironment = startupResult.sandboxEnvironment;
6343
6760
  state.releaseSharedClientLease = startupResult.releaseSharedClientLease;
6344
6761
  state.restartContextEngineCodexThread = startupResult.restartContextEngineCodexThread;
6762
+ await attemptTools.captureCronCreatorToolAllowlist();
6345
6763
  pluginAppServer = startupResult.pluginAppServer;
6346
6764
  toolBridge.setRemoteWorkspaceFileReader?.(({ path, maxBytes, workspaceRoot, signal, timeoutMs }) => readBoundedCodexRemoteWorkspaceFile({
6347
6765
  client: startupResult.client,
@@ -6389,7 +6807,7 @@ async function startCodexAttemptRuntime(resources) {
6389
6807
  clientId: state.client.getInstanceId()
6390
6808
  }
6391
6809
  });
6392
- if (applyNoContextEngineContinuityProjection(state.thread.lifecycle.action, state.thread)) await rebuildCodexTurnPromptTextFromCurrentProjection();
6810
+ if (await applyNoContextEngineContinuityProjection(state.thread.lifecycle.action, state.thread)) await rebuildCodexTurnPromptTextFromCurrentProjection();
6393
6811
  trajectoryRecorder?.recordEvent("session.started", {
6394
6812
  sessionFile: params.sessionFile,
6395
6813
  threadId: state.thread.threadId,
@@ -6397,13 +6815,6 @@ async function startCodexAttemptRuntime(resources) {
6397
6815
  workspaceDir: effectiveWorkspace,
6398
6816
  toolCount: flattenCodexDynamicToolFunctions(toolBridge.specs).length
6399
6817
  });
6400
- recordCodexTrajectoryContext(trajectoryRecorder, {
6401
- attempt: params,
6402
- cwd: effectiveCwd,
6403
- developerInstructions: joinPresentSections(buildRenderedCodexDeveloperInstructions(), attemptTools.configuredMcp?.diagnosticNotice),
6404
- prompt: turnState.codexTurnPromptText,
6405
- tools: toolBridge.availableSpecs
6406
- });
6407
6818
  connection.mutable.pluginAppServer = pluginAppServer;
6408
6819
  } catch (error) {
6409
6820
  await runCleanupStep("codex-start-failure-hook-fallback", activateNativePreToolUseFailureFallback);
@@ -6413,7 +6824,6 @@ async function startCodexAttemptRuntime(resources) {
6413
6824
  await runCleanupStep("codex-start-failure-native-hook-relay", () => nativeHookRelay?.unregister());
6414
6825
  await runCleanupStep("codex-start-failure-sandbox-release", releaseSandboxExecEnvironment);
6415
6826
  await runCleanupStep("codex-start-failure-shared-client-release", releaseSharedClientLeaseAndRetireOneShotClient);
6416
- await runCleanupStep("codex-start-failure-abort-listener", () => params.abortSignal?.removeEventListener("abort", abortFromUpstream));
6417
6827
  throw error instanceof CodexThreadPolicyHandoffError ? error : state.executionDisconnectError ?? error;
6418
6828
  }
6419
6829
  }
@@ -6424,15 +6834,14 @@ function isAuthorityResolutionOperationAbort(error, signal) {
6424
6834
  }
6425
6835
  async function prepareCodexAttemptTools(runtime) {
6426
6836
  const { connection, bundleMcpThreadConfig, bundleManifestRegistry, runtimeParams, effectiveRuntimeModelId, nativeToolSurfaceEnabled, nativeProviderWebSearchSupport, hookChannelId, codexMcpToolOverrides, authenticatedScheduledMode, configuredMcpSurface, canResolveScheduledConfiguredMcpCreatorAuthority } = runtime;
6427
- const { params, preDynamicStartupStages, mutable, startupAuthProfileId, resolvedWorkspace, effectiveWorkspace, effectiveCwd, sandboxSessionKey, contextSessionKey, sandbox, sessionPermissionPolicy, runAbortController, sessionAgentId, policyAgentId, pluginConfig, profilerEnabled, agentDir } = connection;
6837
+ const { params, preDynamicStartupStages, mutable, resolvedWorkspace, effectiveWorkspace, effectiveCwd, sandboxSessionKey, contextSessionKey, sandbox, sessionPermissionPolicy, runAbortController, sessionAgentId, policyAgentId, pluginConfig, profilerEnabled, agentDir } = connection;
6428
6838
  const preDynamicSummary = preDynamicStartupStages.snapshot();
6429
- if (shouldWarnCodexDynamicToolBuildStageSummary(preDynamicSummary)) embeddedAgentLog.warn(`codex app-server pre-dynamic startup timings runId=${params.runId} sessionId=${params.sessionId} totalMs=${preDynamicSummary.totalMs} stages=${formatCodexDynamicToolBuildStageSummary(preDynamicSummary)}`, {
6839
+ if (shouldWarnCodexDynamicToolBuildStageSummary(preDynamicSummary, profilerEnabled)) embeddedAgentLog.warn(`codex app-server pre-dynamic startup timings runId=${params.runId} sessionId=${params.sessionId} totalMs=${preDynamicSummary.totalMs} stages=${formatCodexDynamicToolBuildStageSummary(preDynamicSummary)}`, {
6430
6840
  runId: params.runId,
6431
6841
  sessionId: params.sessionId,
6432
6842
  totalMs: preDynamicSummary.totalMs,
6433
6843
  stages: preDynamicSummary.stages,
6434
6844
  hasStartupBinding: Boolean(mutable.startupBinding?.threadId),
6435
- startupAuthProfileId: startupAuthProfileId ?? null,
6436
6845
  bundleMcpDiagnosticCount: bundleMcpThreadConfig.diagnostics.length,
6437
6846
  nativeToolSurfaceEnabled
6438
6847
  });
@@ -6495,7 +6904,6 @@ async function prepareCodexAttemptTools(runtime) {
6495
6904
  const canResolveScheduledCodexAppAuthority = appCreatorCapture.supported;
6496
6905
  const requiresScheduledCodexAppAuthority = appCreatorCapture.required;
6497
6906
  const canResolveAnyScheduledCreatorAuthority = canResolveScheduledConfiguredMcpCreatorAuthority || requiresScheduledCodexAppAuthority;
6498
- let toolBridge;
6499
6907
  let creatorAuthorityPromise;
6500
6908
  let resolveCreatorAuthorityImpl;
6501
6909
  const runtimeYieldCompletionClaim = {};
@@ -6516,8 +6924,9 @@ async function prepareCodexAttemptTools(runtime) {
6516
6924
  pluginConfig,
6517
6925
  profilerEnabled,
6518
6926
  ...params.cronCreatorAuthorityUnavailableReason === "queued-local-operator" && bundleMcpThreadConfig.staticServerNames.length > 0 ? { cronCreatorAuthorityUnavailableReason: "queued-local-operator-configured-mcp" } : {},
6519
- onYieldDetected: (acknowledgment) => {
6927
+ onYieldDetected: (message, acknowledgment) => {
6520
6928
  toolState.yieldDetected = true;
6929
+ toolState.yieldMessage = message;
6521
6930
  toolState.yieldAcknowledgment = acknowledgment;
6522
6931
  },
6523
6932
  claimYieldCompletion: () => runtimeYieldCompletionClaim.current?.() ?? false,
@@ -6540,8 +6949,9 @@ async function prepareCodexAttemptTools(runtime) {
6540
6949
  let nativeSpecs;
6541
6950
  if (hasCodexNativeToolCatalog(mutable.startupBinding)) {
6542
6951
  runAbortController.signal.throwIfAborted();
6543
- params.hostCapabilities.assertActive();
6952
+ connection.assertCurrent();
6544
6953
  const client = await connection.attemptClientFactory({
6954
+ assertCurrent: connection.assertCurrent,
6545
6955
  startOptions: connection.appServer.start,
6546
6956
  authProfileId: connection.startupClientAuthProfileId,
6547
6957
  agentDir,
@@ -6556,16 +6966,20 @@ async function prepareCodexAttemptTools(runtime) {
6556
6966
  agentDir,
6557
6967
  assertCurrent: () => {
6558
6968
  runAbortController.signal.throwIfAborted();
6559
- params.hostCapabilities.assertActive();
6969
+ connection.assertCurrent();
6560
6970
  }
6561
6971
  });
6562
6972
  } finally {
6563
6973
  releaseLeasedSharedCodexAppServerClient(client);
6564
6974
  }
6565
6975
  }
6976
+ let requireExplicitMessageTarget;
6566
6977
  const tools = await buildDynamicTools({
6567
6978
  ...commonToolParams,
6568
6979
  registerRunCleanup: (cleanup) => runCleanups.push(cleanup),
6980
+ onMessageToolTargetResolved: (required) => {
6981
+ requireExplicitMessageTarget = required;
6982
+ },
6569
6983
  cronCreatorToolAllowlistRef: cronCreatorToolAllowlist,
6570
6984
  cronCreatorToolAllowlistCaptureRef,
6571
6985
  onPersistentWebSearchPolicyResolved: (allowed) => {
@@ -6588,7 +7002,7 @@ async function prepareCodexAttemptTools(runtime) {
6588
7002
  sessionId: params.sessionId,
6589
7003
  runId: params.runId,
6590
7004
  agentId: policyAgentId,
6591
- agentDir: agentDir ?? resolveAgentDir(params.config ?? {}, sessionAgentId),
7005
+ agentDir: agentDir ?? resolveAgentDir$2(params.config ?? {}, sessionAgentId),
6592
7006
  agentAccountId: params.agentAccountId,
6593
7007
  messageProvider: params.messageProvider ?? params.messageChannel,
6594
7008
  messageChannel: params.messageChannel,
@@ -6672,6 +7086,15 @@ async function prepareCodexAttemptTools(runtime) {
6672
7086
  warn: (message) => embeddedAgentLog.warn(message)
6673
7087
  }) : void 0;
6674
7088
  let scopedMcpTools = void 0;
7089
+ const disposeMcpTools = async () => {
7090
+ for (const [step, materialized] of [["codex-scoped-mcp-dispose", scopedMcpTools], ["codex-configured-mcp-dispose", configuredMcp]]) await runAgentCleanupStep({
7091
+ runId: params.runId,
7092
+ sessionId: params.sessionId,
7093
+ step,
7094
+ log: embeddedAgentLog,
7095
+ cleanup: async () => materialized?.dispose()
7096
+ });
7097
+ };
6675
7098
  try {
6676
7099
  scopedMcpTools = authenticatedScheduledMode ? void 0 : await materializeRequesterScopedMcpToolsForHarnessRun({
6677
7100
  sessionId: params.sessionId,
@@ -6722,27 +7145,24 @@ async function prepareCodexAttemptTools(runtime) {
6722
7145
  ...params.agentAccountId ? { turnSourceAccountId: params.agentAccountId } : {},
6723
7146
  ...params.currentThreadTs !== void 0 ? { turnSourceThreadId: params.currentThreadTs } : {}
6724
7147
  };
6725
- toolBridge = createCodexDynamicToolBridge({
7148
+ const toolBridge = createCodexDynamicToolBridge({
6726
7149
  tools: toolsWithScopedMcp,
6727
7150
  registeredTools: registeredWithScopedMcp,
6728
7151
  registeredSpecs: nativeSpecs,
6729
7152
  signal: runAbortController.signal,
6730
7153
  computerContextEpoch,
6731
7154
  loading: resolveCodexDynamicToolsLoadingForRuntime(pluginConfig, effectiveRuntimeModelId, { connectionClass: connection.appServer.connectionClass }),
6732
- directToolNames: resolveCodexDynamicToolDirectNames(params, isHostScopedAgentToolActive("openclaw")),
7155
+ directToolNames: resolveCodexDynamicToolDirectNames(params, registeredWithScopedMcp, isHostScopedAgentToolActive("openclaw")),
6733
7156
  hookContext
6734
7157
  });
6735
- await captureFinalCodexCronCreatorToolAllowlist(cronCreatorToolAllowlist, cronCreatorToolAllowlistCaptureRef, toolBridge.availableTools);
6736
- if (!authenticatedScheduledMode && bundleMcpThreadConfig.staticServerNames.length > 0 && !canResolveScheduledConfiguredMcpCreatorAuthority) delete cronCreatorToolAllowlistCaptureRef.value;
6737
- if (requiresScheduledCodexAppAuthority) delete cronCreatorToolAllowlistCaptureRef.value;
7158
+ const captureCronCreatorToolAllowlist = async () => {
7159
+ await captureFinalCodexCronCreatorToolAllowlist(cronCreatorToolAllowlist, cronCreatorToolAllowlistCaptureRef, toolBridge.availableTools, { nativeToolSurfaceEnabled });
7160
+ if (!authenticatedScheduledMode && bundleMcpThreadConfig.staticServerNames.length > 0 && !canResolveScheduledConfiguredMcpCreatorAuthority) delete cronCreatorToolAllowlistCaptureRef.value;
7161
+ if (requiresScheduledCodexAppAuthority) delete cronCreatorToolAllowlistCaptureRef.value;
7162
+ };
6738
7163
  if (canResolveAnyScheduledCreatorAuthority) resolveCreatorAuthorityImpl = async (options) => {
6739
7164
  options?.signal?.throwIfAborted();
6740
7165
  if (codexAppAuthorityUnavailableReason) throw new Error(codexAppAuthorityUnavailableReason);
6741
- if (!toolBridge) throw new Error("cron creator authority resolver lost the active tool bridge");
6742
- const authorityTools = [];
6743
- const captureRef = {};
6744
- await captureFinalCodexCronCreatorToolAllowlist(authorityTools, captureRef, toolBridge.availableTools);
6745
- if (!captureRef.value) throw new Error("cron creator authority snapshot did not produce provenance");
6746
7166
  const appSource = scheduledAppAuthoritySourceRef.current;
6747
7167
  const runtimeAuthority = canResolveScheduledCodexAppAuthority && scheduledCodexAppAuth ? appSource ? await captureScheduledCodexAppAuthority({
6748
7168
  ...appSource,
@@ -6751,46 +7171,39 @@ async function prepareCodexAttemptTools(runtime) {
6751
7171
  }) : (() => {
6752
7172
  throw new Error("Codex app authority is unavailable before the exact creator thread is active. Retry this automation mutation from the current owner turn.");
6753
7173
  })() : void 0;
6754
- if (!canResolveScheduledConfiguredMcpCreatorAuthority) {
6755
- options?.signal?.throwIfAborted();
6756
- return Object.freeze({
6757
- tools: Object.freeze(authorityTools.map((entry) => Object.freeze(entry))),
6758
- provenance: Object.freeze(captureRef.value),
6759
- ...runtimeAuthority ? { runtimeAuthority } : {}
6760
- });
6761
- }
6762
- const authorityRuntimeId = `cron-authority:${params.runId}`;
6763
7174
  let materialized;
6764
7175
  try {
6765
- materialized = await materializeStaticMcpToolsForHarnessRun({
6766
- sessionId: authorityRuntimeId,
6767
- agentId: sessionAgentId,
6768
- workspaceDir: effectiveWorkspace,
6769
- agentDir: policyContext.agentDir,
6770
- cfg: params.config,
6771
- manifestRegistry: bundleManifestRegistry,
6772
- reservedToolNames: toolBridge.availableTools.map((tool) => tool.name),
6773
- toolsAllow: params.toolsAllow,
6774
- toolOverrides: codexMcpToolOverrides,
6775
- autoApproveCodexAppServerApprovals: shouldAutoApproveCodexAppServerApprovals(connection.appServer),
6776
- projectedMcpServers: bundleMcpThreadConfig.configPatch?.mcp_servers,
6777
- policyContext,
6778
- warn: (message) => embeddedAgentLog.warn(message),
6779
- retireSessionRuntimeAfterDispose: true
6780
- });
6781
- } catch (error) {
6782
- const detail = error instanceof Error ? error.message : String(error);
6783
- throw new Error(`Configured MCP discovery failed while resolving inherited automation authority: ${detail}. Retry after the server is available, or provide an explicit finite toolsAllow list containing only currently visible tools; no automation changes were saved.`, { cause: error });
6784
- }
6785
- try {
7176
+ if (canResolveScheduledConfiguredMcpCreatorAuthority) try {
7177
+ materialized = await materializeStaticMcpToolsForHarnessRun({
7178
+ sessionId: `cron-authority:${params.runId}`,
7179
+ agentId: sessionAgentId,
7180
+ workspaceDir: effectiveWorkspace,
7181
+ agentDir: policyContext.agentDir,
7182
+ cfg: params.config,
7183
+ manifestRegistry: bundleManifestRegistry,
7184
+ reservedToolNames: toolBridge.availableTools.map((tool) => tool.name),
7185
+ toolsAllow: params.toolsAllow,
7186
+ toolOverrides: codexMcpToolOverrides,
7187
+ autoApproveCodexAppServerApprovals: shouldAutoApproveCodexAppServerApprovals(connection.appServer),
7188
+ projectedMcpServers: bundleMcpThreadConfig.configPatch?.mcp_servers,
7189
+ policyContext,
7190
+ warn: (message) => embeddedAgentLog.warn(message),
7191
+ retireSessionRuntimeAfterDispose: true
7192
+ });
7193
+ } catch (error) {
7194
+ const detail = error instanceof Error ? error.message : String(error);
7195
+ throw new Error(`Configured MCP discovery failed while resolving inherited automation authority: ${detail}. Retry after the server is available, or provide an explicit finite toolsAllow list containing only currently visible tools; no automation changes were saved.`, { cause: error });
7196
+ }
6786
7197
  options?.signal?.throwIfAborted();
6787
- if (materialized.diagnosticNotice) throw new Error(`${materialized.diagnosticNotice} Sign in to the affected MCP server and retry, or provide an explicit finite toolsAllow list containing only currently visible tools. No automation changes were saved.`);
6788
- const projectedConfiguredMcp = projectCodexExecutableDynamicTools({
7198
+ if (materialized?.diagnosticNotice) throw new Error(`${materialized.diagnosticNotice} Sign in to the affected MCP server and retry, or provide an explicit finite toolsAllow list containing only currently visible tools. No automation changes were saved.`);
7199
+ const configuredTools = materialized ? projectCodexExecutableDynamicTools({
6789
7200
  tools: filterCodexDynamicTools(materialized.tools, pluginConfig),
6790
7201
  hookContext
6791
- });
6792
- await captureFinalCodexCronCreatorToolAllowlist(authorityTools, captureRef, [...toolBridge.availableTools, ...projectedConfiguredMcp.availableTools]);
6793
- if (!captureRef.value) throw new Error("configured MCP authority snapshot did not produce provenance");
7202
+ }).availableTools : [];
7203
+ const authorityTools = [];
7204
+ const captureRef = {};
7205
+ await captureFinalCodexCronCreatorToolAllowlist(authorityTools, captureRef, [...toolBridge.availableTools, ...configuredTools], { nativeToolSurfaceEnabled });
7206
+ if (!captureRef.value) throw new Error("cron creator authority snapshot did not produce provenance");
6794
7207
  options?.signal?.throwIfAborted();
6795
7208
  return Object.freeze({
6796
7209
  tools: Object.freeze(authorityTools.map((entry) => Object.freeze(entry))),
@@ -6798,17 +7211,18 @@ async function prepareCodexAttemptTools(runtime) {
6798
7211
  ...runtimeAuthority ? { runtimeAuthority } : {}
6799
7212
  });
6800
7213
  } finally {
6801
- await materialized.dispose();
7214
+ await materialized?.dispose();
6802
7215
  }
6803
7216
  };
6804
7217
  return {
6805
7218
  tools: toolsWithScopedMcp,
6806
7219
  registeredTools: registeredWithScopedMcp,
7220
+ requireExplicitMessageTarget,
6807
7221
  scopedMcpTools,
6808
7222
  configuredMcp,
7223
+ disposeMcpTools,
6809
7224
  configuredMcpOwnershipVersion: configuredMcpSurface === "scheduled" ? 1 : void 0,
6810
- cronCreatorToolAllowlist,
6811
- cronCreatorToolAllowlistCaptureRef,
7225
+ captureCronCreatorToolAllowlist,
6812
7226
  scheduledAppAuthoritySourceRef,
6813
7227
  dynamicToolParams,
6814
7228
  compactionPlanState,
@@ -6823,8 +7237,7 @@ async function prepareCodexAttemptTools(runtime) {
6823
7237
  runtimeYieldCompletionClaim
6824
7238
  };
6825
7239
  } catch (error) {
6826
- await scopedMcpTools?.dispose();
6827
- await configuredMcp?.dispose();
7240
+ await disposeMcpTools();
6828
7241
  throw error;
6829
7242
  }
6830
7243
  }
@@ -6907,11 +7320,22 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
6907
7320
  error.name = "AbortError";
6908
7321
  throw error;
6909
7322
  };
7323
+ const prepareWorkspaceReferences = () => {
7324
+ const references = prepareCodexWorkspaceReferences(resourceState.client, resourceState.thread.threadId, workspaceBootstrapContext.promptContext);
7325
+ prompt.refreshWorkspaceReferences(references.include);
7326
+ return references;
7327
+ };
6910
7328
  const startCodexTurn = async () => {
6911
7329
  const activeTurnRoute = await ensureCurrentThreadRoute();
7330
+ assertCodexSessionRuntimeOwnership(resourceState.thread, params.expectedSessionRuntimeOwnership);
6912
7331
  const turnAppServer = withCodexAppServerFastModeServiceTier(connection.mutable.pluginAppServer, runtimeParams);
6913
7332
  connection.mutable.pluginAppServer = turnAppServer;
6914
- const turnStartParams = buildTurnStartParams(runtimeParams, {
7333
+ const references = prepareWorkspaceReferences();
7334
+ const referencesRetained = turnState.codexTurnPromptText.includes(workspaceBootstrapContext.promptContext ?? "");
7335
+ const turnStartParams = buildTurnStartParams({
7336
+ ...runtimeParams,
7337
+ images: [...prompt.contextImages, ...runtimeParams.images ?? []]
7338
+ }, {
6915
7339
  threadId: resourceState.thread.threadId,
6916
7340
  cwd: resourceState.codexExecutionCwd,
6917
7341
  appServer: turnAppServer,
@@ -6928,9 +7352,18 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
6928
7352
  skillsCollaborationInstructions: context.skillsCollaborationInstructions,
6929
7353
  memoryCollaborationInstructions: workspaceBootstrapContext.memoryCollaborationInstructions,
6930
7354
  preserveNativeTurnSettings: usesSupervisionConnection,
7355
+ messageToolAvailable: toolBridge.availableTools.some((tool) => tool.name === "message"),
7356
+ requireExplicitMessageTarget: attemptTools.requireExplicitMessageTarget,
6931
7357
  sessionStatusAvailable: toolBridge.availableTools.some((tool) => tool.name === "session_status")
6932
7358
  });
6933
7359
  codexModelCallDiagnostics.setRequestPayloadBytes(utf8JsonByteLength(turnStartParams));
7360
+ recordCodexTrajectoryContext(resources.trajectoryRecorder, {
7361
+ attempt: params,
7362
+ cwd: connection.effectiveCwd,
7363
+ developerInstructions: joinPresentSections(buildRenderedCodexDeveloperInstructions(), attemptTools.configuredMcp?.diagnosticNotice),
7364
+ prompt: turnState.codexTurnPromptText,
7365
+ tools: toolBridge.availableSpecs
7366
+ });
6934
7367
  state.latestStartupErrorNotification = void 0;
6935
7368
  state.rateLimitsRevisionBeforeLastTurnStart = readCodexRateLimitsRevision(resourceState.client);
6936
7369
  activeTurnRoute.armTurn();
@@ -6949,9 +7382,12 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
6949
7382
  try {
6950
7383
  const startedTurn = assertCodexTurnStartResponse(await resourceState.client.request("turn/start", turnStartParams, {
6951
7384
  timeoutMs: params.timeoutMs,
6952
- signal: runAbortController.signal
7385
+ signal: runAbortController.signal,
7386
+ assertCurrent: connection.assertCurrent
6953
7387
  }));
6954
7388
  acceptedTurnId = startedTurn.turn.id;
7389
+ connection.assertCurrent();
7390
+ if (referencesRetained) references.accepted();
6955
7391
  throwIfTurnStartAcceptedAfterAbort();
6956
7392
  return startedTurn;
6957
7393
  } catch (error) {
@@ -6980,6 +7416,7 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
6980
7416
  if (await waitForActiveNativeTurnCompletion()) await resourceState.turnRoute?.drain();
6981
7417
  else if (!runAbortController.signal.aborted) embeddedAgentLog.warn("codex app-server active native turn did not complete before turn/start wait timed out", { threadId: resourceState.thread.threadId });
6982
7418
  }
7419
+ prepareWorkspaceReferences();
6983
7420
  const buildLlmInputEvent = () => ({
6984
7421
  runId: params.runId,
6985
7422
  sessionId: params.sessionId,
@@ -6988,7 +7425,7 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
6988
7425
  systemPrompt: buildRenderedCodexDeveloperInstructions(),
6989
7426
  prompt: turnState.codexTurnPromptText,
6990
7427
  historyMessages: prompt.codexModelInputHistoryMessages,
6991
- imagesCount: params.images?.length ?? 0,
7428
+ imagesCount: prompt.contextImages.length + (params.images?.length ?? 0),
6992
7429
  tools
6993
7430
  });
6994
7431
  return {
@@ -7004,7 +7441,7 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
7004
7441
  const { context, turnState, systemPromptReport } = prompt;
7005
7442
  const { runtime, historyState, hookContext, hookContextWindowFields, hookRunner } = context;
7006
7443
  const { connection, runtimeParams, effectiveRuntimeProviderId, effectiveRuntimeModelId } = runtime;
7007
- const { params, usesSupervisionConnection, runAbortController, activeContextEngine, bindingStore, bindingIdentity, appServer, attemptStartedAt, startupAuthProfileId, abortFromUpstream } = connection;
7444
+ const { params, usesSupervisionConnection, runAbortController, activeContextEngine, bindingStore, bindingIdentity, appServer, attemptStartedAt, startupAuthProfileId } = connection;
7008
7445
  const { state, turnIdRef } = turnRuntime;
7009
7446
  const { waitForActiveNativeTurnCompletion } = notifications;
7010
7447
  const { codexModelCallDiagnostics, startCodexTurn, buildLlmInputEvent } = requestRuntime;
@@ -7040,52 +7477,51 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
7040
7477
  error: turnStartError,
7041
7478
  contextEngineActive: Boolean(activeContextEngine),
7042
7479
  thread: resourceState.thread
7043
- }) && resourceState.restartContextEngineCodexThread) {
7480
+ }) && resourceState.restartContextEngineCodexThread) try {
7481
+ assertCodexBindingMayBeReplaced(resourceState.thread, "retrying an overflow on a fresh native thread", params.expectedSessionRuntimeOwnership);
7044
7482
  embeddedAgentLog.warn("codex app-server context-engine turn overflowed on resume; retrying with fresh thread", {
7045
7483
  threadId: resourceState.thread.threadId,
7046
7484
  error: formatErrorMessage(turnStartError)
7047
7485
  });
7048
- try {
7049
- if (!await bindingStore.mutate(bindingIdentity, {
7050
- kind: "clear",
7051
- threadId: resourceState.thread.threadId
7052
- })) embeddedAgentLog.warn("codex app-server preserved newer context-engine binding after resume overflow; skipping fresh retry", {
7053
- threadId: resourceState.thread.threadId,
7054
- error: formatErrorMessage(turnStartError)
7055
- });
7056
- else {
7057
- resourceState.thread = await resourceState.restartContextEngineCodexThread();
7058
- const retryBinding = await bindingStore.read(bindingIdentity);
7059
- if (retryBinding && retryBinding.threadId === resourceState.thread.threadId && retryBinding.contextEngine?.projection) {
7060
- await bindingStore.mutate(bindingIdentity, {
7061
- kind: "patch",
7062
- threadId: retryBinding.threadId,
7063
- patch: { contextEngine: {
7064
- ...retryBinding.contextEngine,
7065
- projection: void 0
7066
- } }
7067
- });
7068
- embeddedAgentLog.info("codex app-server cleared stale context-engine projection after overflow retry", {
7069
- threadId: resourceState.thread.threadId,
7070
- previousEpoch: retryBinding.contextEngine.projection.epoch
7071
- });
7072
- }
7073
- emitCodexAppServerEvent(params, {
7074
- stream: "codex_app_server.lifecycle",
7075
- data: {
7076
- phase: "thread_ready_retry",
7077
- threadId: resourceState.thread.threadId
7078
- }
7486
+ if (!await bindingStore.mutate(bindingIdentity, {
7487
+ kind: "clear",
7488
+ threadId: resourceState.thread.threadId
7489
+ })) embeddedAgentLog.warn("codex app-server preserved newer context-engine binding after resume overflow; skipping fresh retry", {
7490
+ threadId: resourceState.thread.threadId,
7491
+ error: formatErrorMessage(turnStartError)
7492
+ });
7493
+ else {
7494
+ resourceState.thread = await resourceState.restartContextEngineCodexThread();
7495
+ const retryBinding = bindingStore.read(bindingIdentity);
7496
+ if (retryBinding && retryBinding.threadId === resourceState.thread.threadId && retryBinding.contextEngine?.projection) {
7497
+ await bindingStore.mutate(bindingIdentity, {
7498
+ kind: "patch",
7499
+ threadId: retryBinding.threadId,
7500
+ patch: { contextEngine: {
7501
+ ...retryBinding.contextEngine,
7502
+ projection: void 0
7503
+ } }
7079
7504
  });
7080
- try {
7081
- turn = await startCodexTurn();
7082
- } catch (retryError) {
7083
- turnStartError = retryError;
7505
+ embeddedAgentLog.info("codex app-server cleared stale context-engine projection after overflow retry", {
7506
+ threadId: resourceState.thread.threadId,
7507
+ previousEpoch: retryBinding.contextEngine.projection.epoch
7508
+ });
7509
+ }
7510
+ emitCodexAppServerEvent(params, {
7511
+ stream: "codex_app_server.lifecycle",
7512
+ data: {
7513
+ phase: "thread_ready_retry",
7514
+ threadId: resourceState.thread.threadId
7084
7515
  }
7516
+ });
7517
+ try {
7518
+ turn = await startCodexTurn();
7519
+ } catch (retryError) {
7520
+ turnStartError = retryError;
7085
7521
  }
7086
- } catch (retrySetupError) {
7087
- turnStartError = retrySetupError;
7088
7522
  }
7523
+ } catch (retrySetupError) {
7524
+ turnStartError = retrySetupError;
7089
7525
  }
7090
7526
  if (turn === void 0) {
7091
7527
  const usageLimitError = await formatCodexTurnStartUsageLimitError({
@@ -7101,7 +7537,7 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
7101
7537
  phase: "turn_start",
7102
7538
  threadId: resourceState.thread.threadId,
7103
7539
  error: message
7104
- });
7540
+ }, params.expectedSessionRuntimeOwnership);
7105
7541
  emitCodexAppServerEvent(params, {
7106
7542
  stream: "codex_app_server.lifecycle",
7107
7543
  data: {
@@ -7180,7 +7616,6 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
7180
7616
  log: embeddedAgentLog,
7181
7617
  cleanup: async () => trajectoryRecorder?.flush()
7182
7618
  });
7183
- params.abortSignal?.removeEventListener("abort", abortFromUpstream);
7184
7619
  await releaseSharedClientLeaseAndRetireOneShotClient();
7185
7620
  if (usageLimitError) {
7186
7621
  await markCodexAuthProfileBlockedFromRateLimits({
@@ -7191,7 +7626,7 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
7191
7626
  return { result: buildCodexTurnStartFailureResult({
7192
7627
  params,
7193
7628
  message: usageLimitError.message,
7194
- promptError: createCodexUsageLimitPromptError(usageLimitError.message),
7629
+ promptError: new CodexUsageLimitPromptError(usageLimitError.message),
7195
7630
  messagesSnapshot,
7196
7631
  systemPromptReport
7197
7632
  }) };
@@ -7299,6 +7734,8 @@ function createCodexAttemptTurnState(resources) {
7299
7734
  terminalTurnNotificationQueued: false,
7300
7735
  sawCodexInterruptMarker: false,
7301
7736
  timeout: void 0,
7737
+ settlementWarning: void 0,
7738
+ pendingSettlementStage: void 0,
7302
7739
  clientClosedPromptError: void 0,
7303
7740
  clientClosedDiagnostic: void 0,
7304
7741
  clientClosedAbort: false,
@@ -7314,6 +7751,7 @@ function createCodexAttemptTurnState(resources) {
7314
7751
  currentTurnHadNonTerminalDynamicToolResult: false
7315
7752
  };
7316
7753
  const { promise: completion, resolve: resolveCompletion } = createDeferred();
7754
+ const settlementExpired = createDeferred();
7317
7755
  const pendingOpenClawDynamicToolCompletionIds = /* @__PURE__ */ new Set();
7318
7756
  const openClawDynamicToolExecutions = createCodexDynamicToolExecutionRegistry();
7319
7757
  const activeTurnItemIds = /* @__PURE__ */ new Set();
@@ -7374,13 +7812,32 @@ function createCodexAttemptTurnState(resources) {
7374
7812
  signal: runAbortController.signal,
7375
7813
  onDeadlineChanged: params.onAttemptDeadlineChanged,
7376
7814
  onTimeout: (timeout) => {
7815
+ const pendingStage = state.pendingSettlementStage ?? projectorRef.current?.settlement.pendingStage ?? "notification_queue";
7816
+ if (timeout.kind === "settlement" && projectorRef.current?.recoverCompletedAnswer()) {
7817
+ state.settlementWarning = {
7818
+ pendingStage,
7819
+ elapsedMs: timeout.elapsedMs,
7820
+ timeoutMs: timeout.timeoutMs
7821
+ };
7822
+ state.projectionClosed = true;
7823
+ trajectoryRecorder?.recordEvent("turn.settlement_warning", {
7824
+ threadId: resourceState.thread.threadId,
7825
+ turnId: turnIdRef.current,
7826
+ ...state.settlementWarning
7827
+ });
7828
+ embeddedAgentLog.warn("codex app-server retaining completed answer after settlement expiry", state.settlementWarning);
7829
+ completeTurn();
7830
+ settlementExpired.resolve();
7831
+ return;
7832
+ }
7377
7833
  state.timeout = timeout;
7378
7834
  projectorRef.current?.markTimedOut();
7379
7835
  const error = /* @__PURE__ */ new Error(timeout.kind === "execution" ? "codex app-server execution budget timed out" : "codex app-server terminal settlement timed out");
7380
7836
  const fields = {
7381
7837
  threadId: resourceState.thread.threadId,
7382
7838
  turnId: turnIdRef.current,
7383
- ...timeout
7839
+ ...timeout,
7840
+ pendingStage
7384
7841
  };
7385
7842
  trajectoryRecorder?.recordEvent(`turn.${timeout.kind}_timeout`, fields);
7386
7843
  embeddedAgentLog.warn(error.message, fields);
@@ -7391,6 +7848,7 @@ function createCodexAttemptTurnState(resources) {
7391
7848
  return {
7392
7849
  state,
7393
7850
  completion,
7851
+ settlementExpired: settlementExpired.promise,
7394
7852
  pendingOpenClawDynamicToolCompletionIds,
7395
7853
  openClawDynamicToolExecutions,
7396
7854
  activeTurnItemIds,
@@ -7407,35 +7865,47 @@ function createCodexAttemptTurnState(resources) {
7407
7865
  //#endregion
7408
7866
  //#region extensions/codex/src/app-server/run-attempt.ts
7409
7867
  async function runCodexAppServerAttempt(params, options) {
7410
- const connection = await prepareCodexAttemptConnection({
7868
+ const preparation = createCodexAttemptPreparationTiming(params);
7869
+ const connection = await preparation.measure("connection", () => prepareCodexAttemptConnection({
7411
7870
  params,
7412
7871
  options
7413
- });
7414
- const runtime = await prepareCodexAttemptRuntime(connection);
7415
- const attemptTools = await prepareCodexAttemptTools(runtime);
7416
- const resources = prepareCodexAttemptResources(await prepareCodexAttemptPrompt(await prepareCodexAttemptContext(runtime, attemptTools)));
7417
- attemptTools.runtimeYieldCompletionClaim.current = () => resources.state.nativeHookRelay?.hasClaimedDirectChild() ?? false;
7418
- await startCodexAttemptRuntime(resources);
7419
- const turnRuntime = createCodexAttemptTurnState(resources);
7872
+ }));
7420
7873
  try {
7421
- const lifecycle = createCodexAttemptLifecycleController(resources, turnRuntime);
7422
- const notifications = createCodexAttemptNotificationController(resources, turnRuntime, lifecycle);
7423
- const { ensureCurrentThreadRoute } = await prepareCodexAttemptRoute(resources, turnRuntime, notifications, createCodexAttemptServerRequestController(resources, turnRuntime, lifecycle).handleServerRequest);
7424
- const turnRequest = await prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurrentThreadRoute, notifications.waitForActiveNativeTurnCompletion);
7425
- const turnStart = await startCodexAttemptTurn(resources, turnRuntime, notifications, turnRequest);
7426
- if ("result" in turnStart) return turnStart.result;
7427
- const activeTurn = activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notifications, turnStart.turn);
7428
- let finalizedResult;
7874
+ const runtime = await preparation.measure("runtime", () => prepareCodexAttemptRuntime(connection));
7875
+ const attemptTools = await preparation.measure("tools", () => prepareCodexAttemptTools(runtime));
7429
7876
  try {
7430
- await activeTurn.ready;
7431
- finalizedResult = await finalizeCodexAttempt(resources, turnRuntime, lifecycle, notifications, turnRequest, activeTurn);
7877
+ const attemptContext = await preparation.measure("context", () => prepareCodexAttemptContext(runtime, attemptTools));
7878
+ const resources = prepareCodexAttemptResources(await preparation.measure("prompt", () => prepareCodexAttemptPrompt(attemptContext)));
7879
+ attemptTools.runtimeYieldCompletionClaim.current = () => resources.state.nativeHookRelay?.hasClaimedDirectChild() ?? false;
7880
+ await preparation.measure("runtime-start", () => startCodexAttemptRuntime(resources));
7881
+ const turnRuntime = createCodexAttemptTurnState(resources);
7882
+ try {
7883
+ const lifecycle = createCodexAttemptLifecycleController(resources, turnRuntime);
7884
+ const notifications = createCodexAttemptNotificationController(resources, turnRuntime, lifecycle);
7885
+ const serverRequests = createCodexAttemptServerRequestController(resources, turnRuntime, lifecycle);
7886
+ const { ensureCurrentThreadRoute } = await preparation.measure("thread-route", () => prepareCodexAttemptRoute(resources, turnRuntime, notifications, serverRequests.handleServerRequest));
7887
+ const turnRequest = await preparation.measure("turn-request", () => prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurrentThreadRoute, notifications.waitForActiveNativeTurnCompletion));
7888
+ preparation.ready();
7889
+ const turnStart = await startCodexAttemptTurn(resources, turnRuntime, notifications, turnRequest);
7890
+ if ("result" in turnStart) return turnStart.result;
7891
+ const activeTurn = activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notifications, turnStart.turn);
7892
+ let finalizedResult;
7893
+ try {
7894
+ await activeTurn.ready;
7895
+ finalizedResult = await finalizeCodexAttempt(resources, turnRuntime, lifecycle, notifications, turnRequest, activeTurn);
7896
+ } finally {
7897
+ await cleanupCodexAttempt(resources, turnRuntime, lifecycle, turnRequest, activeTurn);
7898
+ }
7899
+ if (resources.state.executionDisconnectError && !connection.terminalState.explicitCancellationObserved) throw resources.state.executionDisconnectError;
7900
+ return finalizedResult;
7901
+ } finally {
7902
+ turnRuntime.deadlines.dispose();
7903
+ }
7432
7904
  } finally {
7433
- await cleanupCodexAttempt(resources, turnRuntime, lifecycle, turnRequest, activeTurn);
7905
+ await attemptTools.disposeMcpTools();
7434
7906
  }
7435
- if (resources.state.executionDisconnectError && !connection.terminalState.explicitCancellationObserved) throw resources.state.executionDisconnectError;
7436
- return finalizedResult;
7437
7907
  } finally {
7438
- turnRuntime.deadlines.dispose();
7908
+ params.abortSignal?.removeEventListener("abort", connection.abortFromUpstream);
7439
7909
  }
7440
7910
  }
7441
7911
  //#endregion