@openclaw/codex 2026.8.1-beta.3 → 2026.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/api.js +5 -6
  2. package/dist/{attempt-client-cleanup-URjUlCHg.js → attempt-client-cleanup-CpVfwctT.js} +47 -5
  3. package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
  4. package/dist/{binding-connection-DAvZwp96.js → binding-connection-CF7BmrDk.js} +2 -2
  5. package/dist/{bounded-turn-4BjrZEwW.js → bounded-turn-DRC3hcjm.js} +16 -20
  6. package/dist/{capabilities-CH3K57v4.js → capabilities-D6m_TdVy.js} +1 -1
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-D0nZ3qtd.js → command-formatters--UToCokS.js} +4 -3
  9. package/dist/{command-handlers-DC86lgT4.js → command-handlers-CLFx2Ypa.js} +114 -79
  10. package/dist/{compact-COqgBRny.js → compact-5xqW1oCh.js} +13 -12
  11. package/dist/{config-CpNeo5AE.js → config-CcYL77O6.js} +350 -288
  12. package/dist/{conversation-binding-data-CW2GuupV.js → conversation-binding-data-BiodpIzq.js} +2 -1
  13. package/dist/doctor-contract-api.js +121 -16
  14. package/dist/{dynamic-tools-fJZhl4CE.js → dynamic-tools-BmeXjwNZ.js} +243 -1002
  15. package/dist/{effective-mcp-catalog-4YKn51aG.js → effective-mcp-catalog-XXQKgK1U.js} +2 -2
  16. package/dist/{event-projector-assistant-message-BwxV4d6N.js → event-projector-assistant-message-C5-Ds8mA.js} +18 -1
  17. package/dist/{event-projector-usage-CLwNEJqW.js → event-projector-usage-B2r5vhWH.js} +9 -14
  18. package/dist/{harness-DoKQcF8n.js → harness-BgeB7LnD.js} +42 -52
  19. package/dist/harness.js +1 -1
  20. package/dist/index.js +308 -306
  21. package/dist/{isolated-completion-DPMtlH9r.js → isolated-completion-CxTus3vZ.js} +4 -4
  22. package/dist/launch-args-sXOAg0SX.js +84 -0
  23. package/dist/{managed-binary-By4FZC6i.js → managed-binary-ZkGodW19.js} +43 -7
  24. package/dist/media-understanding-provider.js +1 -1
  25. package/dist/model-catalog-DULBcniB.js +101 -0
  26. package/dist/{models-CZw1wClT.js → models-DaWfETQk.js} +27 -29
  27. package/dist/{native-subagent-monitor-GGq3RnOo.js → native-subagent-monitor-vXYUZH37.js} +226 -129
  28. package/dist/{node-exec-server.runtime-Bsu1LdFZ.js → node-exec-server.runtime-DirKVZ5h.js} +9 -6
  29. package/dist/{plugin-approval-roundtrip-C75Yiiwc.js → plugin-approval-roundtrip-CCWQGlAI.js} +2 -1
  30. package/dist/{plugin-marketplace-discovery-CSWDkMrP.js → plugin-marketplace-discovery-BXjIFEiw.js} +38 -46
  31. package/dist/{plugin-metadata-cache-BXH5NF-4.js → plugin-metadata-cache-DQQm4gWT.js} +163 -136
  32. package/dist/{protocol-validators-VAVyEnDp.js → protocol-validators-Bg5rpNxk.js} +148 -5
  33. package/dist/{rate-limits-BI1dMHDG.js → rate-limits-BqHEHXQJ.js} +3 -2
  34. package/dist/{sandbox-guard-C1qEeZ5C.js → request-BWxweEdK.js} +183 -2
  35. package/dist/{run-attempt-BV3HzuCf.js → run-attempt-BVLE4Gbi.js} +706 -517
  36. package/dist/{runtime-artifact-BvyT1uyd.js → runtime-artifact-D9pYp4zL.js} +3 -3
  37. package/dist/{session-binding-BTrLf9Qm.js → session-binding-D0zinCP1.js} +195 -122
  38. package/dist/{session-catalog-ezP_UcRM.js → session-catalog-eMhVm0Ql.js} +448 -313
  39. package/dist/session-catalog-home-id-Dt2GiG5o.js +112 -0
  40. package/dist/{session-cli-B1hKvfkI.js → session-cli-B2lyjJA0.js} +1 -1
  41. package/dist/session-history-C5J1AKzG.js +87 -0
  42. package/dist/session-permission-policy-ScnAyG90.js +160 -0
  43. package/dist/session-retirement-CtUtUTr9.js +90 -0
  44. package/dist/{session-upstream-marker-D_2npHlA.js → session-upstream-marker-gqrYIRHk.js} +2 -3
  45. package/dist/settled-turn-context-Bk3IUwod.js +290 -0
  46. package/dist/settled-turn-finalizer-BXLN9PfJ.js +104 -0
  47. package/dist/{shared-client-Dw-rzii0.js → shared-client-B1tT5U6r.js} +1738 -806
  48. package/dist/{side-question-DbXkJZzg.js → side-question-CZ-mCw-t.js} +76 -43
  49. package/dist/{thread-lifecycle-DKdrnTMX.js → thread-lifecycle-CGGKP720.js} +991 -455
  50. package/dist/{thread-ownership-i2NBwDUL.js → thread-ownership-RkaoZCmQ.js} +18 -8
  51. package/dist/{plugin-app-cache-key-DMNQ8Zi8.js → thread-resume-BFt1-ZDa.js} +55 -8
  52. package/dist/{transcript-mirror-BU0_LKAL.js → transcript-mirror-1ep4XjzF.js} +124 -18
  53. package/dist/transport-stdio-NT_Dpd9V.js +724 -0
  54. package/dist/{upstream-prompt-provenance-CFmsKwr9.js → upstream-prompt-provenance-LphB8slG.js} +8 -4
  55. package/dist/{upstream-session-fork-BDVJCLAD.js → upstream-session-fork-Dc8BFExw.js} +76 -90
  56. package/dist/{usage-9dKJCgcy.js → usage-DUhd58gI.js} +3 -3
  57. package/dist/{web-search-provider.runtime-DCdx4DGu.js → web-search-provider.runtime-BEmlE7ov.js} +3 -3
  58. package/openclaw.plugin.json +19 -1
  59. package/package.json +8 -8
  60. package/dist/app-server-policy-bg5QPSVP.js +0 -25
  61. package/dist/command-rpc-xUIIonsO.js +0 -90
  62. package/dist/incognito-session-ET_ufDH-.js +0 -2
  63. package/dist/model-catalog-CmGoqVKB.js +0 -48
  64. package/dist/request-DSK0W6YI.js +0 -157
  65. package/dist/session-catalog-home-id-BNwAJ9D7.js +0 -18
  66. package/dist/session-retirement-CE4iMiLh.js +0 -36
  67. package/dist/settled-turn-finalizer-Dx1onIqg.js +0 -328
  68. package/dist/thread-resume-CdJk2Ehf.js +0 -30
  69. package/dist/transport-stdio-DhoGy33Z.js +0 -99
@@ -1,42 +1,48 @@
1
- import { A as resolveOpenClawExecPolicyForCodexAppServer, C as parseAllowedApprovalPoliciesFromCodexRequirements, D as selectGuardianApprovalPolicy, E as readCodexRequirementsToml, O as selectGuardianApprovalsReviewer, S as resolveCodexModelBackedReviewerPolicyContext, T as parseAllowedSandboxModesFromCodexRequirements, b as canUseCodexModelBackedApprovalsReviewerForModel, d as withMcpElicitationsApprovalPolicy, f as isCodexPairedNodeRemoteExecPlacementSandbox, g as resolveCodexPluginsPolicy, h as readCodexPluginConfig, j as selectGuardianSandbox, k as selectUserApprovalsReviewer, l as resolveCodexAppServerNetworkProxy, m as isCodexSandboxExecServerEnabled, o as resolveCodexComputerUseConfig, p as isCodexRemoteExecPlacementSandbox, r as resolveCodexAppServerHomeScope, u as shouldAutoApproveCodexAppServerApprovals, w as parseAllowedApprovalsReviewersFromCodexRequirements } from "./config-CpNeo5AE.js";
2
- import { $ as rememberCodexRateLimitsRead, A as isCodexAppServerApprovalRequest, B as CodexAppServerRpcError, C as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, D as resolveCodexTurnTerminalIdleTimeoutMs, E as resolveCodexTurnCompletionIdleTimeoutMs, I as isCodexAppServerRequestTimeoutError, M as isCodexAppServerConnectionClosedError, N as isCodexAppServerIndeterminateRequestCancellationError, O as withCodexStartupTimeout, P as isCodexAppServerIndeterminateTransportError, Q as readRecentCodexRateLimits, R as resolveCodexAppServerClientInstanceId, T as resolveCodexTurnAssistantCompletionIdleTimeoutMs, V as createCodexElicitationResponse, W as ensureCodexAppServerClientRuntime, Y as retainCodexAppServerLiveThread, Z as readCodexRateLimitsRevision, a as getLeasedSharedCodexAppServerClient, at as resolveCodexAppServerPreparedAuthHandoff, b as CodexAppServerStartupError, et as resolveCodexAppServerAuthAccountCacheKey, f as retainSharedCodexAppServerClientIfCurrent, ft as resolveCodexAppServerHomeDir, g as withAbortableTimeout, i as createIsolatedCodexAppServerClient, it as resolveCodexAppServerPreparedApiKeyCacheKey, j as isCodexAppServerBrokenPipeError, k as getCodexAppServerClientInstanceId, l as releaseLeasedSharedCodexAppServerClient, lt as flattenCodexDynamicToolFunctions, n as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, nt as resolveCodexAppServerAuthProfileIdForAgent, o as getSharedCodexAppServerClient, p as retireSharedCodexAppServerClientIfCurrent, q as protectCodexAppServerLiveThread, rt as resolveCodexAppServerFallbackApiKeyCacheKey, s as isCodexAppServerStartSelectionChangedError, t as clearSharedCodexAppServerClientIfCurrent, tt as resolveCodexAppServerAuthProfileId, ut as isJsonObject, w as resolveCodexStartupTimeoutMs, x as isCodexAppServerStartupError, y as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS } from "./shared-client-Dw-rzii0.js";
3
- import { c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, i as createCodexSessionGenerationSupersededError, l as resolveCodexRunSessionBindingAuthority, u as scopeCodexRunBindingStore } from "./session-binding-BTrLf9Qm.js";
4
- import { A as shouldSynthesizeToolProgressForItem, B as readNullableString, E as itemStatus, F as readItem, I as readItemString, N as readCodexErrorNotificationMessage, O as shouldClearTerminalPresentationForNativeItem, T as itemName, V as splitPlanText, a as closeCodexStartupClientBestEffort, d as unsubscribeCodexThreadBestEffort, f as CODEX_APP_SERVER_NATIVE_TURN_WAIT_TIMEOUT_MS, g as readCodexNotificationThreadId, h as isCodexNotificationForTurn, j as extractRawAssistantText, l as retireCodexAppServerClientAfterTimedOutTurn, m as CodexProjectionDiagnostics, o as interruptCodexTurnAndWaitBestEffort, p as getCodexAppServerTurnRouter, r as CodexAppServerUnsafeSubscriptionError, t as CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS, u as retireUnsafeCodexTurnClientBestEffort, z as readNonNegativeInteger } from "./attempt-client-cleanup-URjUlCHg.js";
5
- import { C as readRawResponseToolCallId, E as updateActiveTurnItemIds, S as readNotificationItemId, T as updateActiveCompletionBlockerItemIds, _ as isReasoningItemCompletionNotification, a as codexExecutionToolName, b as isTerminalTurnStatus, c as isAssistantCompletionReleaseNotification, d as isNativeToolProgressNotification, f as isPendingOpenClawDynamicToolCompletionNotification, g as isRawToolOutputCompletionNotification, h as isRawReasoningCompletionNotification, l as isCodexTurnAbortMarkerNotification, m as isRawFunctionToolOutputCompletionNotification, o as describeNotificationActivity, p as isRawAssistantProgressNotification, r as projectCodexThreadUsageUpdate, s as isAssistantCommentaryCompletionNotification, t as CodexResponseCompletionProjection, u as isFileChangePatchUpdatedNotification, v as isReasoningProgressNotification, w as shouldDisarmAssistantCompletionIdleWatch, x as readCodexNotificationItem, y as isRetryableErrorNotification } from "./event-projector-usage-CLwNEJqW.js";
6
- import { l as readCodexTurn, o as assertCodexTurnStartResponse, s as readCodexDynamicToolCallParams, u as readCodexTurnCompletedNotification } from "./protocol-validators-VAVyEnDp.js";
7
- import { n as buildCodexAppServerRuntimeFingerprint, o as defaultCodexAppInventoryCache, r as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-DMNQ8Zi8.js";
8
- import { B as neutralizeCodexExplicitMentionSigils, D as codexLegacyDynamicToolsFingerprint, E as codexDynamicToolsFingerprint, F as resolveCodexDynamicToolsLoadingForRuntime, G as buildCodexPluginAppsConfigPatchFromPolicyContext, H as resolveCodexContextEngineProjectionMaxChars, I as buildContextEngineBinding, J as buildCodexPluginThreadConfigTimeoutFallback, K as buildCodexPluginThreadConfig, L as isContextEngineBindingCompatible, M as isSystemAgentOnlyCodexDynamicToolAllowlist, O as filterCodexDynamicTools, Q as shouldBuildCodexPluginThreadConfig, R as buildCodexContinuityCalibration, T as areCodexDynamicToolFingerprintsCompatible, U as resolveCodexContextEngineProjectionReserveTokens, V as projectContextEngineAssemblyForCodex, W as resolveCodexContinuityProjectionMaxChars, X as buildPluginAppPolicyContext, Z as mergeCodexThreadConfigs, a as isCodexAppServerProfilerEnabled, f as resolveCodexWebSearchPlan, i as startOrResumeThread, it as resolveRecoverableCodexPluginConfigKeys, j as isMessageOnlyCodexSourceReply, n as buildTurnStartParams, p as buildDeveloperInstructions, q as buildCodexPluginThreadConfigInputFingerprint, r as buildCodexUserInput, t as buildTurnCollaborationMode, v as resolveCodexAppServerThreadModelSelection, z as fitCodexProjectedContextForTurnStart } from "./thread-lifecycle-DKdrnTMX.js";
9
- import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
10
- import { i as readUpstreamUserText, n as attachUpstreamUserText, r as readMirrorIdentity, t as attachCodexMirrorIdentity } from "./upstream-prompt-provenance-CFmsKwr9.js";
11
- import { a as buildCodexUserPromptMessage, c as readCodexMirrorSourceFingerprint, i as mirrorPromptAtTurnStartBestEffort, l as serializeCodexMirrorSourceEvidence, n as createCodexAppServerUserMessagePersistenceNotifier, o as promptSnapshot, t as codexTranscriptMirrorRuntime } from "./transcript-mirror-BU0_LKAL.js";
12
- import { t as CODEX_CONTROL_METHODS } from "./capabilities-CH3K57v4.js";
13
- import { a as shouldRefreshCodexRateLimitsForUsageLimitMessage, i as resolveCodexUsageLimitResetAtMs, n as formatCodexUsageLimitErrorMessage } from "./rate-limits-BI1dMHDG.js";
14
- import { r as formatCodexDisplayText } from "./command-formatters-D0nZ3qtd.js";
15
- import { t as resolveCodexAppServerForModelProvider } from "./app-server-policy-bg5QPSVP.js";
16
- import { n as resolveCodexBindingAppServerConnection } from "./binding-connection-DAvZwp96.js";
17
- import { n as prepareCodexAppServerAuthBinding } from "./auth-binding-B4FfbcCg.js";
1
+ import { C as isCodexRemoteExecPlacementSandbox, D as resolveOpenClawExecPolicyForCodexAppServer, E as resolveCodexPluginsPolicy, P as resolveCodexAppServerHomeDir, S as isCodexPairedNodeRemoteExecPlacementSandbox, T as readCodexPluginConfig, d as withMcpElicitationsApprovalPolicy, m as resolveCodexModelBackedReviewerPolicyContext, o as resolveCodexComputerUseConfig, p as canUseCodexModelBackedApprovalsReviewerForModel, r as resolveCodexAppServerHomeScope, u as shouldAutoApproveCodexAppServerApprovals, v as readCodexRequirementsToml, w as isCodexSandboxExecServerEnabled } from "./config-CcYL77O6.js";
2
+ import { $ as CodexAppServerRpcError, A as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, B as resolveCodexTurnTerminalIdleTimeoutMs, Dt as readCodexRateLimitsRevision, Ft as resolveCodexAppServerFallbackApiKeyCacheKey, G as isCodexAppServerConnectionClosedError, H as getCodexAppServerClientInstanceId, Ht as isJsonObject, I as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, It as resolveCodexAppServerPreparedApiKeyCacheKey, J as isCodexAppServerOverloadError, K as isCodexAppServerIndeterminateRequestCancellationError, L as resolveCodexStartupTimeoutMs, Lt as resolveCodexAppServerPreparedAuthHandoff, M as TURN_FINALIZE_DRAIN_ABORT_GRACE_MS, Mt as resolveCodexAppServerAuthProfileId, N as TURN_TERMINAL_SETTLEMENT_IDLE_TIMEOUT_MS, Nt as resolveCodexAppServerAuthProfileIdForAgent, Ot as readRecentCodexRateLimits, P as isCodexAppServerStartupError, Q as resolveCodexAppServerClientInstanceId, R as resolveCodexTurnAssistantCompletionIdleTimeoutMs, Tt as withAbortableTimeout, U as isCodexAppServerApprovalRequest, V as withCodexStartupTimeout, Vt as flattenCodexDynamicToolFunctions, W as isCodexAppServerBrokenPipeError, X as isCodexAppServerRequestTimeoutError, _t as ensureCodexAppServerClientRuntime, a as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, at as resolveDynamicToolCallTimeoutMs, c as getLeasedSharedCodexAppServerClient, ct as shouldReleaseTurnAfterTerminalDynamicTool, dt as resolveCodexToolAbortTerminalReason, et as createCodexElicitationResponse, f as readCodexAppServerClientDesktopGenerationFingerprint, h as releaseLeasedSharedCodexAppServerClient, i as clearSharedCodexAppServerClientIfCurrent, it as isMatchingDynamicToolTerminalDiagnostic, j as CodexAppServerStartupError, jt as resolveCodexAppServerAuthAccountCacheKey, kt as rememberCodexRateLimitsRead, l as getSharedCodexAppServerClient, lt as toCodexDynamicToolProgressResponse, nt as hasPendingDynamicToolTerminalDiagnostic, ot as resolveTerminalDynamicToolBatchAction, q as isCodexAppServerIndeterminateTransportError, rt as isDynamicToolTerminalDiagnosticEvent, s as createIsolatedCodexAppServerClient, st as shouldBlockTerminalReleaseForNonTerminalDynamicToolResult, tt as handleDynamicToolCallWithTimeout, u as isCodexAppServerStartSelectionChangedError, ut as toCodexDynamicToolProtocolResponse, v as retainSharedCodexAppServerClientIfCurrent, xt as protectCodexAppServerLiveThread, y as retireSharedCodexAppServerClientIfCurrent, z as resolveCodexTurnCompletionIdleTimeoutMs } from "./shared-client-B1tT5U6r.js";
3
+ import { c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, i as createCodexSessionGenerationSupersededError, l as resolveCodexRunSessionBindingAuthority, u as scopeCodexRunBindingStore } from "./session-binding-D0zinCP1.js";
4
+ import { c as assertCodexTurnStartResponse, d as readCodexTurn, f as readCodexTurnCompletedNotification, l as readCodexDynamicToolCallParams } from "./protocol-validators-Bg5rpNxk.js";
5
+ import { B as readNonNegativeInteger, D as itemStatus, E as itemName, H as splitPlanText, I as readItem, L as readItemString, M as extractRawAssistantText, P as readCodexErrorNotificationMessage, V as readNullableString, _ as readCodexNotificationThreadId, a as closeCodexStartupClientBestEffort, d as terminateCodexBackgroundTerminals, f as unsubscribeCodexThreadBestEffort, g as isCodexNotificationForTurn, h as CodexProjectionDiagnostics, j as shouldSynthesizeToolProgressForItem, k as shouldClearTerminalPresentationForNativeItem, l as retireCodexAppServerClientAfterTimedOutTurn, m as getCodexAppServerTurnRouter, o as interruptCodexTurnAndWaitBestEffort, p as CODEX_APP_SERVER_NATIVE_TURN_WAIT_TIMEOUT_MS, r as CodexAppServerUnsafeSubscriptionError, t as CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS, u as retireUnsafeCodexTurnClientBestEffort } from "./attempt-client-cleanup-CpVfwctT.js";
6
+ import { C as readRawResponseToolCallId, E as updateActiveTurnItemIds, S as readNotificationItemId, T as updateActiveCompletionBlockerItemIds, _ as isReasoningItemCompletionNotification, a as codexExecutionToolName, b as isTerminalTurnStatus, c as isAssistantCompletionReleaseNotification, d as isNativeToolProgressNotification, f as isPendingOpenClawDynamicToolCompletionNotification, g as isRawToolOutputCompletionNotification, h as isRawReasoningCompletionNotification, l as isCodexTurnAbortMarkerNotification, m as isRawFunctionToolOutputCompletionNotification, o as describeNotificationActivity, p as isRawAssistantProgressNotification, r as projectCodexThreadUsageUpdate, s as isAssistantCommentaryCompletionNotification, t as CodexResponseCompletionProjection, u as isFileChangePatchUpdatedNotification, v as isReasoningProgressNotification, w as shouldDisarmAssistantCompletionIdleWatch, x as readCodexNotificationItem, y as isRetryableErrorNotification } from "./event-projector-usage-B2r5vhWH.js";
7
+ import { i as buildCodexPluginAppCacheKey, r as buildCodexAppServerRuntimeFingerprint, s as defaultCodexAppInventoryCache } from "./thread-resume-BFt1-ZDa.js";
8
+ import { $ as resolveCodexContinuityProjectionMaxChars, B as isMessageOnlyCodexSourceReply, D as resolveCodexAppServerThreadModelSelection, F as codexDynamicToolsFingerprint, G as buildContextEngineBinding, I as codexLegacyDynamicToolsFingerprint, J as fitCodexProjectedContextForTurnStart, K as isContextEngineBindingCompatible, L as filterCodexDynamicTools, P as areCodexDynamicToolFingerprintsCompatible, Q as resolveCodexContextEngineProjectionReserveTokens, S as buildDeveloperInstructions, V as isSystemAgentOnlyCodexDynamicToolAllowlist, W as resolveCodexDynamicToolsLoadingForRuntime, X as projectContextEngineAssemblyForCodex, Y as neutralizeCodexExplicitMentionSigils, Z as resolveCodexContextEngineProjectionMaxChars, a as isCodexAppServerProfilerEnabled, at as buildPluginAppPolicyContext, c as buildCodexNativeHookRelayConfig, d as emitCodexNativePreToolUseFailureDiagnostic, et as buildCodexPluginAppsConfigPatchFromPolicyContext, f as resolveCodexNativeHookRelayEvents, i as startOrResumeThread, l as buildCodexNativeHookRelayDisabledConfig, m as scheduleCodexNativeHookRelayUnregister, n as buildTurnStartParams, nt as buildCodexPluginThreadConfigInputFingerprint, ot as mergeCodexThreadConfigs, p as resolveCodexNativeHookRelayTtlMs, pt as resolveRecoverableCodexPluginConfigKeys, q as buildCodexContinuityCalibration, r as buildCodexUserInput, rt as buildCodexPluginThreadConfigTimeoutFallback, s as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, st as shouldBuildCodexPluginThreadConfig, t as buildTurnCollaborationMode, tt as buildCodexPluginThreadConfig, u as createCodexNativeHookRelay, x as resolveCodexWebSearchPlan } from "./thread-lifecycle-CGGKP720.js";
9
+ import { l as isIncognitoSessionKey, r as retainCodexAppServerBindingSubscription } from "./thread-ownership-RkaoZCmQ.js";
10
+ import { r as readMirrorIdentity, t as attachCodexMirrorIdentity } from "./upstream-prompt-provenance-LphB8slG.js";
11
+ 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-1ep4XjzF.js";
12
+ import { t as CODEX_CONTROL_METHODS } from "./capabilities-D6m_TdVy.js";
13
+ import { a as shouldRefreshCodexRateLimitsForUsageLimitMessage, i as resolveCodexUsageLimitResetAtMs, n as formatCodexUsageLimitErrorMessage } from "./rate-limits-BqHEHXQJ.js";
14
+ import { r as formatCodexDisplayText } from "./command-formatters--UToCokS.js";
15
+ import { a as resolveCodexAppServerForModelProvider, i as resolveCodexSessionPermissionCwd, n as applyCodexSessionPermissionPolicy, r as resolveCodexEffectiveSessionPermissionPolicy } from "./session-permission-policy-ScnAyG90.js";
16
+ import { n as resolveCodexBindingAppServerConnection } from "./binding-connection-CF7BmrDk.js";
17
+ import { n as prepareCodexAppServerAuthBinding } from "./auth-binding-Ds_QVm-X.js";
18
18
  import { t as persistCodexContextCompactionActivity } from "./context-compaction-activity-BOeJf7yo.js";
19
- import { n as createAssistantMessage, r as createAssistantMirrorMessage, t as createAssistantCommentaryMessage } from "./event-projector-assistant-message-BwxV4d6N.js";
20
- import { a as CodexToolProgressProjection, c as isCodexCommandBearingToolCall, d as sanitizeCodexToolResponse, i as readCodexMirroredSessionHistoryMessages, l as resolveCodexToolProgressDetailMode, n as CodexEventProjection, o as shouldEmitTranscriptToolProgress, r as CodexToolTranscriptProjection, s as inferCodexDynamicToolMeta, t as codexNativeSubagentMonitorRuntime, u as sanitizeCodexToolArguments } from "./native-subagent-monitor-GGq3RnOo.js";
21
- import { A as buildCodexNativeHookRelayConfig, B as isMatchingDynamicToolTerminalDiagnostic, C as shouldRequireCodexSandboxExecServerEnvironment, D as CodexNativeToolLifecycleProjector, E as readBoundedCodexRemoteWorkspaceFile, F as resolveCodexNativeHookRelayTtlMs, G as toCodexDynamicToolProgressResponse, H as resolveTerminalDynamicToolBatchAction, I as scheduleCodexNativeHookRelayUnregister, K as toCodexDynamicToolProtocolResponse, L as handleDynamicToolCallWithTimeout, M as createCodexNativeHookRelay, N as emitCodexNativePreToolUseFailureDiagnostic, P as resolveCodexNativeHookRelayEvents, R as hasPendingDynamicToolTerminalDiagnostic, S as shouldEnableCodexAppServerNativeToolSurface, U as shouldBlockTerminalReleaseForNonTerminalDynamicToolResult, V as resolveDynamicToolCallTimeoutMs, W as shouldReleaseTurnAfterTerminalDynamicTool, _ as resolveCodexAppServerHookChannelId, a as emitDynamicToolStartedDiagnostic, c as resolveCodexProviderWebSearchSupport, d as releaseCodexSandboxExecServerEnvironment, f as buildDynamicTools, g as resolveCodexAppServerExecutionCwd, h as formatCodexDynamicToolBuildStageSummary, i as emitDynamicToolErrorDiagnostic, j as buildCodexNativeHookRelayDisabledConfig, k as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, m as disableCodexPluginThreadConfig, n as projectCodexExecutableDynamicTools, o as emitDynamicToolTerminalDiagnostic, p as createCodexDynamicToolBuildStageTracker, q as resolveCodexToolAbortTerminalReason, r as routeCodexAppServerElicitationRequest, s as handleCodexAppServerApprovalRequest, t as createCodexDynamicToolBridge, u as ensureCodexSandboxExecServerEnvironment, v as resolveCodexExternalSandboxPolicyForOpenClawSandbox, w as shouldWarnCodexDynamicToolBuildStageSummary, x as resolveCodexSandboxEnvironmentSelection, y as resolveCodexMessageToolProvider, z as isDynamicToolTerminalDiagnosticEvent } from "./dynamic-tools-fJZhl4CE.js";
22
- import { a as runCodexComputerUseLiveTest, n as ensureCodexComputerUse, o as killStaleComputerUseMcpChildren, t as defaultCodexPluginMetadataCache } from "./plugin-metadata-cache-BXH5NF-4.js";
19
+ import { i as createAssistantMirrorMessage, n as createAssistantCommentaryMessage, r as createAssistantMessage, t as createAssistantAsyncMessage } from "./event-projector-assistant-message-C5-Ds8mA.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-vXYUZH37.js";
21
+ import { n as readCodexMirroredSessionHistoryMessages } from "./session-history-C5J1AKzG.js";
22
+ import { C as shouldRequireCodexSandboxExecServerEnvironment, D as CodexNativeToolLifecycleProjector, E as readBoundedCodexRemoteWorkspaceFile, S as shouldEnableCodexAppServerNativeToolSurface, _ as resolveCodexAppServerHookChannelId, a as emitDynamicToolStartedDiagnostic, c as resolveCodexProviderWebSearchSupport, d as releaseCodexSandboxExecServerEnvironment, f as buildDynamicTools, g as resolveCodexAppServerExecutionCwd, h as formatCodexDynamicToolBuildStageSummary, i as emitDynamicToolErrorDiagnostic, m as disableCodexPluginThreadConfig, n as projectCodexExecutableDynamicTools, o as emitDynamicToolTerminalDiagnostic, p as createCodexDynamicToolBuildStageTracker, r as routeCodexAppServerElicitationRequest, s as handleCodexAppServerApprovalRequest, t as createCodexDynamicToolBridge, u as ensureCodexSandboxExecServerEnvironment, v as resolveCodexExternalSandboxPolicyForOpenClawSandbox, w as shouldWarnCodexDynamicToolBuildStageSummary, x as resolveCodexSandboxEnvironmentSelection, y as resolveCodexMessageToolProvider } from "./dynamic-tools-BmeXjwNZ.js";
23
+ import { n as captureCodexSettledTurnFinalizationContext } from "./settled-turn-context-Bk3IUwod.js";
24
+ import { a as runCodexComputerUseLiveTest, n as ensureCodexComputerUse, t as defaultCodexPluginMetadataCache } from "./plugin-metadata-cache-DQQm4gWT.js";
23
25
  import { asBoolean, asFiniteNumber, asOptionalRecord, hasNonEmptyString, normalizeOptionalString, readNonBlankString, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
24
26
  import fs from "node:fs/promises";
25
27
  import path from "node:path";
26
28
  import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
27
29
  import { asDateTimestampMs, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
28
- import { AgentHarnessPreflightError, CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, FAST_MODE_AUTO_PROGRESS_KIND, agentHarnessAttemptTerminal, agentHarnessStructuredInput, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentEndSideEffects, bootstrapHarnessContextEngine, buildBootstrapContextForFiles, buildHarnessContextEngineRuntimeContext, buildWatchedSessionsHarnessContext, callGatewayTool, cancelPendingAgentQuestionForSession, claimPendingAgentQuestionAnswer, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, embeddedAgentLog, emitAgentEvent, emptyAgentHarnessUserInputAnswers, formatErrorMessage, formatFastModeAutoProgressText, getAgentHarnessHookRunner, isActiveHarnessContextEngine, isHostScopedAgentToolActive, loadCodexBundleMcpThreadConfig, materializeRequesterScopedMcpToolsForHarnessRun, prepareHarnessNativeMcpAppPreview, projectAgentHarnessTranscriptMessageForDisplay, resolveAgentDir, resolveAgentHarnessBeforePromptBuildResult, resolveAgentRunAbortLifecycleFields, resolveBootstrapFilesForRun, resolveContextEngineOwnerPluginId, resolveFastModeForElapsed, resolveSandboxContext, resolveSessionAgentIds, resolveUserPath, runAgentCleanupStep, runAgentEndSideEffects, runAgentHarnessAfterCompactionHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
30
+ import { AgentHarnessPreflightError, CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, FAST_MODE_AUTO_PROGRESS_KIND, agentHarnessAttemptTerminal, agentHarnessStructuredInput, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentEndSideEffects, bootstrapHarnessContextEngine, buildBootstrapContextForFiles, buildEmbeddedForegroundPromptContext, buildHarnessContextEngineRuntimeContext, buildWatchedSessionsHarnessContext, callGatewayTool, cancelPendingAgentQuestionForSession, claimPendingAgentQuestionAnswer, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, detectAndLoadAgentHarnessPromptImages, embeddedAgentLog, emitAgentEvent, emptyAgentHarnessUserInputAnswers, formatErrorMessage, formatFastModeAutoProgressText, getAgentHarnessHookRunner, hasBeforeToolCallPolicy, isActiveHarnessContextEngine, isHostScopedAgentToolActive, loadCodexBundleMcpThreadConfig, materializeRequesterScopedMcpToolsForHarnessRun, prepareHarnessNativeMcpAppPreview, projectAgentHarnessTranscriptMessageForDisplay, resolveAgentDir, resolveAgentHarnessBeforePromptBuildResult, resolveAgentRunAbortLifecycleFields, resolveAttemptFsWorkspaceOnly, resolveBootstrapFilesForRun, resolveContextEngineOwnerPluginId, resolveFastModeForElapsed, resolveSandboxContext, resolveUserPath, runAgentCleanupStep, runAgentEndSideEffects, runAgentHarnessAfterCompactionHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
29
31
  import crypto, { createHash } from "node:crypto";
30
- import { hostname } from "node:os";
31
32
  import { markAuthProfileBlockedUntil, resolveAgentDir as resolveAgentDir$1, resolveAgentWorkspaceDir } from "openclaw/plugin-sdk/agent-runtime";
33
+ import { resolveAgentConfig as resolveAgentConfig$1, resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
32
34
  import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
33
35
  import { parseSqliteSessionFileMarker } from "openclaw/plugin-sdk/session-store-runtime";
36
+ import { addAbortListener } from "node:events";
34
37
  import { truncateUtf16Safe, truncateUtf8Prefix } from "openclaw/plugin-sdk/text-utility-runtime";
35
- import { isPathInside, isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
36
- import { captureFinalCodexCronCreatorToolAllowlist, materializeStaticMcpToolsForScheduledHarnessRun, resolveCodexMcpToolOverridesForAgent } from "openclaw/plugin-sdk/codex-mcp-projection";
37
38
  import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent, emitTrustedDiagnosticEventWithPrivateData, freezeDiagnosticTraceContext, onInternalDiagnosticEvent, resolveDiagnosticModelContentCapturePolicy } from "openclaw/plugin-sdk/diagnostic-runtime";
39
+ import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
40
+ import { captureFinalCodexCronCreatorToolAllowlist, materializeStaticMcpToolsForScheduledHarnessRun, resolveCodexMcpToolOverridesForAgent, resolveCodexTtsProvenanceTransfer } from "openclaw/plugin-sdk/codex-mcp-projection";
41
+ import { hasPromptImageInput } from "openclaw/plugin-sdk/session-transcript-runtime";
38
42
  import { Buffer as Buffer$1 } from "node:buffer";
43
+ import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
39
44
  import { prepareMemorySystemPromptAddition } from "openclaw/plugin-sdk/core";
45
+ import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-local-roots";
40
46
  import { isSilentReplyPayloadText } from "openclaw/plugin-sdk/reply-chunking";
41
47
  import { generatedImageAssetFromBase64 } from "openclaw/plugin-sdk/image-generation";
42
48
  import { resolveGeneratedMediaMaxBytes } from "openclaw/plugin-sdk/media-generation-runtime";
@@ -68,6 +74,12 @@ function createCodexSteeringQueue(params) {
68
74
  let sendChain = Promise.resolve();
69
75
  let sealedError;
70
76
  let closedError;
77
+ const assertActive = () => {
78
+ const unavailableError = closedError ?? sealedError;
79
+ if (unavailableError) throw unavailableError;
80
+ params.signal.throwIfAborted();
81
+ params.assertActive();
82
+ };
71
83
  const clearBatchTimer = () => {
72
84
  if (batchTimer) {
73
85
  clearTimeout(batchTimer);
@@ -120,19 +132,20 @@ function createCodexSteeringQueue(params) {
120
132
  const sendBatch = async (items) => {
121
133
  const liveItems = items.filter((item) => !item.settled);
122
134
  if (liveItems.length === 0) return;
123
- const unavailableError = closedError ?? sealedError ?? (params.signal.aborted ? /* @__PURE__ */ new Error("codex app-server steering queue aborted") : void 0);
124
- if (unavailableError) {
125
- for (const item of liveItems) rejectItem(item, unavailableError);
126
- throw unavailableError;
127
- }
128
- const clientUserMessageId = `openclaw:${params.turnId}:steer:${++batchSequence}`;
129
- const batch = { items: liveItems };
130
- dispatchedBatches.set(clientUserMessageId, batch);
135
+ let clientUserMessageId;
131
136
  try {
137
+ assertActive();
138
+ const input = [];
139
+ for (const item of liveItems) {
140
+ input.push(...await params.prepareMessage(item.text, item));
141
+ assertActive();
142
+ }
143
+ clientUserMessageId = `openclaw:${params.turnId}:steer:${++batchSequence}`;
144
+ dispatchedBatches.set(clientUserMessageId, { items: liveItems });
132
145
  await params.client.request("turn/steer", {
133
146
  threadId: params.threadId,
134
147
  expectedTurnId: params.turnId,
135
- input: liveItems.flatMap((item) => buildCodexUserInput(item.text, item.images)),
148
+ input,
136
149
  clientUserMessageId
137
150
  }, {
138
151
  timeoutMs: params.requestTimeoutMs,
@@ -140,8 +153,8 @@ function createCodexSteeringQueue(params) {
140
153
  });
141
154
  for (const item of liveItems) reportItemAcceptance(item, true);
142
155
  } catch (error) {
143
- dispatchedBatches.delete(clientUserMessageId);
144
- const acceptedUnconfirmed = isCodexAppServerIndeterminateRequestCancellationError(error) || isCodexAppServerIndeterminateTransportError(error);
156
+ if (clientUserMessageId) dispatchedBatches.delete(clientUserMessageId);
157
+ const acceptedUnconfirmed = clientUserMessageId !== void 0 && (isCodexAppServerIndeterminateRequestCancellationError(error) || isCodexAppServerIndeterminateTransportError(error));
145
158
  for (const item of liveItems) {
146
159
  if (acceptedUnconfirmed) reportItemAcceptance(item, true);
147
160
  rejectItem(item, error);
@@ -175,15 +188,12 @@ function createCodexSteeringQueue(params) {
175
188
  rejectDelivery = reject;
176
189
  });
177
190
  const item = {
191
+ ...options,
178
192
  acceptance: "open",
179
193
  text,
180
- images: options?.images,
181
- isInboundUserMessage: options?.isInboundUserMessage,
182
- onQueueAccepted: options?.onQueueAccepted,
183
194
  resolve: resolveDelivery,
184
195
  reject: rejectDelivery,
185
- settled: false,
186
- userTurnTranscriptRecorder: options?.userTurnTranscriptRecorder
196
+ settled: false
187
197
  };
188
198
  pendingMessages.add(item);
189
199
  return {
@@ -195,10 +205,11 @@ function createCodexSteeringQueue(params) {
195
205
  if (params.signal.aborted) abortQueue();
196
206
  return {
197
207
  async queue(text, options) {
198
- const unavailableError = closedError ?? sealedError ?? (params.signal.aborted ? /* @__PURE__ */ new Error("codex app-server steering queue aborted") : void 0);
199
- if (unavailableError) {
208
+ try {
209
+ assertActive();
210
+ } catch (error) {
200
211
  options?.onQueueAccepted?.(false);
201
- throw unavailableError;
212
+ throw error;
202
213
  }
203
214
  const { item, delivery } = createPendingMessage(text, options);
204
215
  batchedMessages.push(item);
@@ -242,15 +253,17 @@ function normalizeCodexSteerDebounceMs(value) {
242
253
  //#endregion
243
254
  //#region extensions/codex/src/app-server/event-projector-assistant.ts
244
255
  var CodexAssistantProjection = class {
245
- constructor(params, emitAgentEvent, matchesToolProgressEcho, nextTranscriptTimestamp) {
256
+ constructor(params, emitAgentEvent, matchesToolProgressEcho, nextTranscriptTimestamp, checkpointCommentary) {
246
257
  this.params = params;
247
258
  this.emitAgentEvent = emitAgentEvent;
248
259
  this.matchesToolProgressEcho = matchesToolProgressEcho;
249
260
  this.nextTranscriptTimestamp = nextTranscriptTimestamp;
261
+ this.checkpointCommentary = checkpointCommentary;
250
262
  this.assistantTextByItem = /* @__PURE__ */ new Map();
251
263
  this.assistantItemOrder = [];
252
264
  this.assistantTimestampByItem = /* @__PURE__ */ new Map();
253
265
  this.assistantPhaseByItem = /* @__PURE__ */ new Map();
266
+ this.assistantDeliveryByItem = /* @__PURE__ */ new Map();
254
267
  this.latestTerminalAssistantCandidateSuperseded = false;
255
268
  this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
256
269
  this.terminalAssistantCandidateEarlierActiveItemIds = /* @__PURE__ */ new Set();
@@ -261,7 +274,9 @@ var CodexAssistantProjection = class {
261
274
  this.assistantStarted = false;
262
275
  this.streamedPartialAssistantItemReplaceable = false;
263
276
  this.persistableAssistantBarrier = 0;
277
+ this.persistedAssistantBoundary = false;
264
278
  this.persistableAssistantBarrierItemIds = /* @__PURE__ */ new Set();
279
+ this.completedAssistantItemIds = /* @__PURE__ */ new Set();
265
280
  }
266
281
  hasCompletedTerminalAssistantText(completedItemIds) {
267
282
  const latestCompletedItemId = this.latestCompletedTerminalAssistantItemId;
@@ -293,7 +308,8 @@ var CodexAssistantProjection = class {
293
308
  if (!delta) return;
294
309
  if (itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
295
310
  const isCommentary = this.isCommentaryAssistantItem(itemId);
296
- if (!isCommentary && itemId !== this.latestTerminalAssistantCandidateItemId) this.markTerminalAssistantCandidateSupersededBy();
311
+ const isAsync = this.isAsyncAssistantItem(itemId);
312
+ if (!isCommentary && !isAsync && itemId !== this.latestTerminalAssistantCandidateItemId) this.markTerminalAssistantCandidateSupersededBy();
297
313
  if (!this.assistantStarted) {
298
314
  this.assistantStarted = true;
299
315
  await this.params.onAssistantMessageStart?.();
@@ -301,6 +317,7 @@ var CodexAssistantProjection = class {
301
317
  this.rememberAssistantItem(itemId);
302
318
  const text = `${this.assistantTextByItem.get(itemId) ?? ""}${delta}`;
303
319
  this.assistantTextByItem.set(itemId, text);
320
+ if (isAsync) return;
304
321
  if (isCommentary) {
305
322
  this.emitCommentaryProgress({
306
323
  itemId,
@@ -332,24 +349,25 @@ var CodexAssistantProjection = class {
332
349
  }
333
350
  recordItemStarted(item, itemId) {
334
351
  this.noteNativeWorkBarrier(item);
335
- if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
336
352
  this.rememberAssistantPhase(item);
353
+ if (item?.type === "agentMessage" && itemId && !this.isNonTerminalAssistantItem(itemId) && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
337
354
  if (item?.type === "agentMessage" && itemId) this.rememberAssistantItem(itemId);
338
- if (itemId && itemId !== this.latestTerminalAssistantCandidateItemId) {
355
+ if (itemId && !this.isNonTerminalAssistantItem(itemId) && itemId !== this.latestTerminalAssistantCandidateItemId) {
339
356
  this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
340
357
  if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
341
358
  }
342
359
  }
343
360
  recordItemCompleted(item, itemId, activeItemIds) {
361
+ if (itemId && item?.type === "agentMessage") this.completedAssistantItemIds.add(itemId);
344
362
  this.noteNativeWorkBarrier(item);
345
- if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
346
- if (itemId) this.latestCompletedItemId = itemId;
347
363
  this.rememberAssistantPhase(item);
348
- if (item?.type === "agentMessage" && !this.isCommentaryAssistantItem(item.id)) {
364
+ if (item?.type === "agentMessage" && itemId && !this.isNonTerminalAssistantItem(itemId) && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
365
+ if (itemId && !this.isNonTerminalAssistantItem(itemId)) this.latestCompletedItemId = itemId;
366
+ if (item?.type === "agentMessage" && !this.isNonTerminalAssistantItem(item.id)) {
349
367
  this.latestCompletedTerminalAssistantItemId = item.id;
350
368
  this.markLatestTerminalAssistantCandidate(item.id, activeItemIds);
351
369
  this.pendingRawTerminalAssistantEchoItemId = item.id;
352
- } else if (itemId) {
370
+ } else if (itemId && !this.isNonTerminalAssistantItem(itemId)) {
353
371
  this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
354
372
  if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
355
373
  }
@@ -362,14 +380,17 @@ var CodexAssistantProjection = class {
362
380
  text: item.text
363
381
  });
364
382
  this.pendingRawCommentaryEchoes += 1;
365
- } else if (item.text && this.isFinalAnswerAssistantItem(item.id)) this.emitAnswerCandidate(item.id, "candidate");
383
+ } else if (item.text && !this.isAsyncAssistantItem(item.id) && this.isFinalAnswerAssistantItem(item.id)) this.emitAnswerCandidate(item.id, "candidate");
384
+ return this.createAsyncDelivery(item.id);
366
385
  }
367
386
  }
368
387
  recordSnapshotItem(item) {
388
+ if (item.type === "agentMessage") this.completedAssistantItemIds.add(item.id);
369
389
  this.rememberAssistantPhase(item);
370
390
  if (item.type === "agentMessage" && typeof item.text === "string") {
371
391
  this.rememberAssistantItem(item.id);
372
392
  this.assistantTextByItem.set(item.id, item.text);
393
+ return this.createAsyncDelivery(item.id);
373
394
  }
374
395
  }
375
396
  handleRawResponseItemCompleted(item, activeItemIds) {
@@ -403,6 +424,7 @@ var CodexAssistantProjection = class {
403
424
  const isIdlessTerminalAssistantAfterCompletedWork = candidateWasSupersededBeforeRaw && rawItemId === void 0 && pendingTerminalAssistantEchoItemId === void 0 && activeItemIds.size === 0;
404
425
  if (text && phase !== "commentary" && candidateWasSupersededBeforeRaw && itemId !== this.streamedPartialAssistantItemId && !isIdlessTerminalAssistantAfterCompletedWork) return;
405
426
  if (phase) this.assistantPhaseByItem.set(itemId, phase);
427
+ this.completedAssistantItemIds.add(itemId);
406
428
  this.rememberAssistantItem(itemId);
407
429
  this.assistantTextByItem.set(itemId, text);
408
430
  if (!text) return;
@@ -418,6 +440,7 @@ var CodexAssistantProjection = class {
418
440
  const audibleAfterHandoff = afterHandoff.filter((text) => !isSilentReplyPayloadText(text));
419
441
  if (audibleAfterHandoff.length > 0) return audibleAfterHandoff;
420
442
  if (afterHandoff.length > 0) return afterHandoff.slice(-1);
443
+ if (this.persistedAssistantBoundary) return [];
421
444
  const recoveredAudible = this.collectPersistableAssistantTexts(0).filter((text) => !isSilentReplyPayloadText(text));
422
445
  if (recoveredAudible.length > 0) return recoveredAudible.slice(-1);
423
446
  const recovered = this.resolveFinalAssistantTextItem()?.text;
@@ -425,16 +448,52 @@ var CodexAssistantProjection = class {
425
448
  }
426
449
  collectCommentaryMessages() {
427
450
  return this.assistantItemOrder.flatMap((itemId) => {
428
- if (!this.isCommentaryAssistantItem(itemId)) return [];
451
+ const message = this.readCommentaryMessage(itemId);
452
+ return message ? [{
453
+ itemId,
454
+ message
455
+ }] : [];
456
+ });
457
+ }
458
+ readCommentaryMessage(itemId) {
459
+ const text = this.assistantTextByItem.get(itemId)?.trim();
460
+ const timestamp = this.assistantTimestampByItem.get(itemId);
461
+ return this.isCommentaryAssistantItem(itemId) && text && timestamp !== void 0 ? createAssistantCommentaryMessage(this.params, text, itemId, timestamp) : void 0;
462
+ }
463
+ collectAsyncMessages() {
464
+ return this.assistantItemOrder.flatMap((itemId) => {
465
+ if (!this.isAsyncAssistantItem(itemId)) return [];
429
466
  const text = this.assistantTextByItem.get(itemId)?.trim();
430
467
  const timestamp = this.assistantTimestampByItem.get(itemId);
431
468
  if (!text || timestamp === void 0) return [];
432
469
  return [{
433
470
  itemId,
434
- message: createAssistantCommentaryMessage(this.params, text, itemId, timestamp)
471
+ message: createAssistantAsyncMessage(this.params, text, itemId, timestamp)
435
472
  }];
436
473
  });
437
474
  }
475
+ collectCompletedAssistantMessages(completedItemIds, options) {
476
+ return this.assistantItemOrder.flatMap((itemId) => {
477
+ const text = this.assistantTextByItem.get(itemId)?.trim();
478
+ if (!completedItemIds.has(itemId) || this.isNonTerminalAssistantItem(itemId) || !text || isSilentReplyPayloadText(text) || this.isToolProgressEchoText(itemId, text)) return [];
479
+ const message = this.createAssistantMessage(text, options);
480
+ const timestamp = this.assistantTimestampByItem.get(itemId) ?? message.timestamp;
481
+ return [{
482
+ itemId,
483
+ message: {
484
+ ...message,
485
+ timestamp
486
+ }
487
+ }];
488
+ });
489
+ }
490
+ markAssistantBoundaryPersisted(itemId) {
491
+ const index = this.assistantItemOrder.indexOf(itemId);
492
+ if (index >= 0) {
493
+ this.persistableAssistantBarrier = Math.max(this.persistableAssistantBarrier, index + 1);
494
+ this.persistedAssistantBoundary = true;
495
+ }
496
+ }
438
497
  finalizeAnswerCandidate(turn) {
439
498
  if (turn.status !== "completed") {
440
499
  this.supersedeVisibleAnswerCandidate();
@@ -444,7 +503,7 @@ var CodexAssistantProjection = class {
444
503
  const authoritativeIndex = turnItems.findLastIndex((item) => {
445
504
  if (item.type !== "agentMessage" || typeof item.text !== "string" || item.text.trim().length === 0) return false;
446
505
  const phase = readItemString(item, "phase");
447
- return phase === "final_answer" || phase === void 0;
506
+ return readItemString(item, "delivery") !== "async" && (phase === "final_answer" || phase === void 0);
448
507
  });
449
508
  const authoritative = authoritativeIndex >= 0 ? turnItems[authoritativeIndex] : void 0;
450
509
  if (turnItems.slice(authoritativeIndex + 1).some(shouldClearTerminalPresentationForNativeItem) || authoritative?.id === this.latestTerminalAssistantCandidateItemId && this.latestTerminalAssistantCandidateSuperseded) {
@@ -462,7 +521,7 @@ var CodexAssistantProjection = class {
462
521
  hasAssistantItemTextForSynthesis() {
463
522
  for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
464
523
  const itemId = this.assistantItemOrder[i];
465
- if (!itemId || this.assistantPhaseByItem.get(itemId) === "commentary") continue;
524
+ if (!itemId || this.isNonTerminalAssistantItem(itemId)) continue;
466
525
  const text = this.assistantTextByItem.get(itemId);
467
526
  if (text && text.length > 0) return true;
468
527
  }
@@ -471,7 +530,7 @@ var CodexAssistantProjection = class {
471
530
  createCurrentAttemptAssistantMessage(options) {
472
531
  for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
473
532
  const itemId = this.assistantItemOrder[i];
474
- if (!itemId || this.isCommentaryAssistantItem(itemId) || !this.assistantTextByItem.has(itemId)) continue;
533
+ if (!itemId || this.isNonTerminalAssistantItem(itemId) || !this.assistantTextByItem.has(itemId)) continue;
475
534
  const text = this.assistantTextByItem.get(itemId) ?? "";
476
535
  const normalizedText = text.trim();
477
536
  if (normalizedText && this.isToolProgressEchoText(itemId, normalizedText)) continue;
@@ -492,10 +551,18 @@ var CodexAssistantProjection = class {
492
551
  if (item?.type !== "agentMessage") return;
493
552
  const phase = readItemString(item, "phase");
494
553
  if (phase) this.assistantPhaseByItem.set(item.id, phase);
554
+ const delivery = readItemString(item, "delivery");
555
+ if (delivery) this.assistantDeliveryByItem.set(item.id, delivery);
495
556
  }
496
557
  isCommentaryAssistantItem(itemId) {
497
558
  return this.assistantPhaseByItem.get(itemId) === "commentary";
498
559
  }
560
+ isAsyncAssistantItem(itemId) {
561
+ return this.assistantDeliveryByItem.get(itemId) === "async";
562
+ }
563
+ isNonTerminalAssistantItem(itemId) {
564
+ return this.isCommentaryAssistantItem(itemId) || this.isAsyncAssistantItem(itemId);
565
+ }
499
566
  isFinalAnswerAssistantItem(itemId) {
500
567
  return this.assistantPhaseByItem.get(itemId) === "final_answer";
501
568
  }
@@ -503,7 +570,7 @@ var CodexAssistantProjection = class {
503
570
  return this.assistantPhaseByItem.get(itemId) === "final_answer";
504
571
  }
505
572
  emitCommentaryProgress(params) {
506
- const progressText = params.text.replace(/\s+/g, " ").trim();
573
+ const progressText = params.text.trim();
507
574
  if (!progressText || this.lastCommentaryProgressTextByItem.get(params.itemId) === progressText) return;
508
575
  this.lastCommentaryProgressTextByItem.set(params.itemId, progressText);
509
576
  this.emitAgentEvent({
@@ -570,7 +637,7 @@ var CodexAssistantProjection = class {
570
637
  const itemId = this.assistantItemOrder[i];
571
638
  if (!itemId) continue;
572
639
  const text = this.assistantTextByItem.get(itemId)?.trim();
573
- if (this.assistantPhaseByItem.get(itemId) === "commentary") continue;
640
+ if (this.isNonTerminalAssistantItem(itemId)) continue;
574
641
  if (text && !this.isToolProgressEchoText(itemId, text)) return {
575
642
  itemId,
576
643
  text
@@ -582,7 +649,7 @@ var CodexAssistantProjection = class {
582
649
  let replaceable = false;
583
650
  for (let index = minIndex; index < this.assistantItemOrder.length; index += 1) {
584
651
  const itemId = this.assistantItemOrder[index];
585
- if (!itemId || this.assistantPhaseByItem.get(itemId) === "commentary") continue;
652
+ if (!itemId || this.isNonTerminalAssistantItem(itemId)) continue;
586
653
  const text = this.assistantTextByItem.get(itemId)?.trim();
587
654
  if (!text || this.isToolProgressEchoText(itemId, text)) continue;
588
655
  if (isSilentReplyPayloadText(text)) {
@@ -606,9 +673,26 @@ var CodexAssistantProjection = class {
606
673
  this.persistableAssistantBarrier = this.assistantItemOrder.length;
607
674
  }
608
675
  rememberAssistantItem(itemId) {
609
- if (!itemId || this.assistantItemOrder.includes(itemId)) return;
610
- this.assistantItemOrder.push(itemId);
611
- this.assistantTimestampByItem.set(itemId, this.nextTranscriptTimestamp());
676
+ if (!itemId) return;
677
+ if (!this.assistantTimestampByItem.has(itemId)) {
678
+ this.assistantItemOrder.push(itemId);
679
+ this.assistantTimestampByItem.set(itemId, this.nextTranscriptTimestamp());
680
+ }
681
+ if (this.isCommentaryAssistantItem(itemId)) this.checkpointCommentary?.(itemId, {
682
+ read: () => this.readCommentaryMessage(itemId),
683
+ ready: () => this.completedAssistantItemIds.has(itemId)
684
+ });
685
+ }
686
+ createAsyncDelivery(itemId) {
687
+ if (!this.isAsyncAssistantItem(itemId)) return;
688
+ const text = this.assistantTextByItem.get(itemId);
689
+ const timestamp = this.assistantTimestampByItem.get(itemId);
690
+ if (!text?.trim() || timestamp === void 0) return;
691
+ return {
692
+ itemId,
693
+ message: createAssistantAsyncMessage(this.params, text, itemId, timestamp),
694
+ text
695
+ };
612
696
  }
613
697
  isToolProgressEchoText(itemId, text) {
614
698
  return this.rawPromotedAssistantItemIds.has(itemId) && this.matchesToolProgressEcho(text);
@@ -618,6 +702,40 @@ function shouldAdvancePersistableAssistantBarrier(item) {
618
702
  return shouldClearTerminalPresentationForNativeItem(item) || item.type === "dynamicToolCall" || item.type === "sleep";
619
703
  }
620
704
  //#endregion
705
+ //#region extensions/codex/src/app-server/event-projector-async-delivery.ts
706
+ /** Owns authorization, exactly-once settlement, and terminal retry for async user messages. */
707
+ var CodexAsyncDeliveryProjection = class {
708
+ constructor(params, threadId, turnId, options) {
709
+ this.params = params;
710
+ this.threadId = threadId;
711
+ this.turnId = turnId;
712
+ this.options = options;
713
+ this.settledItemIds = /* @__PURE__ */ new Set();
714
+ this.pendingDeliveries = /* @__PURE__ */ new Map();
715
+ }
716
+ allows(item, reportUnauthorized = false) {
717
+ if (item?.type !== "agentMessage" || item.delivery !== "async") return true;
718
+ const allowed = this.params.disableTools !== true && (this.options.asyncUserMessageAllowed ?? (this.params.toolsAllow === void 0 || this.params.toolsAllow.some((name) => name === "*" || name === "message")));
719
+ if (!allowed && reportUnauthorized) embeddedAgentLog.warn("blocked unauthorized codex async user message", {
720
+ itemId: item.id,
721
+ threadId: this.threadId,
722
+ turnId: this.turnId
723
+ });
724
+ return allowed;
725
+ }
726
+ pending() {
727
+ return [...this.pendingDeliveries.values()];
728
+ }
729
+ async deliver(delivery) {
730
+ if (this.settledItemIds.has(delivery.itemId)) return;
731
+ const settlement = await this.options.onAsyncDelivery?.(delivery);
732
+ if (settlement === "settled") {
733
+ this.settledItemIds.add(delivery.itemId);
734
+ this.pendingDeliveries.delete(delivery.itemId);
735
+ } else if (settlement === "retry") this.pendingDeliveries.set(delivery.itemId, delivery);
736
+ }
737
+ };
738
+ //#endregion
621
739
  //#region extensions/codex/src/app-server/event-projector-media.ts
622
740
  const GENERATED_IMAGE_MEDIA_SUBDIR = "tool-image-generation";
623
741
  var CodexGeneratedMediaProjection = class {
@@ -911,45 +1029,55 @@ function reasoningMethodOrder(method) {
911
1029
  const attemptTerminal = agentHarnessAttemptTerminal;
912
1030
  //#endregion
913
1031
  //#region extensions/codex/src/app-server/event-projector-snapshot.ts
914
- const CODEX_META_KEY$1 = "__openclaw";
915
- function readTurnTaintMetadata(message) {
916
- return asOptionalRecord(CODEX_META_KEY$1 in message ? message[CODEX_META_KEY$1] : void 0);
917
- }
918
- function applyStickyTurnTaint(messages) {
919
- let tainted = false;
920
- return messages.map((message) => {
921
- if (message.role === "user") {
922
- tainted = false;
923
- return message;
924
- }
925
- const metadata = readTurnTaintMetadata(message);
926
- tainted ||= metadata?.turnTainted === true || metadata?.resultContentSource === "network";
927
- return message.role === "assistant" && tainted ? {
928
- ...message,
929
- __openclaw: {
930
- ...metadata,
931
- turnTainted: true
932
- }
933
- } : message;
934
- });
935
- }
936
1032
  function buildCodexMessagesSnapshot(params) {
937
1033
  const messages = promptSnapshot(params.runParams, params.turnId, params.upstreamUserText);
938
1034
  if (params.reasoningText) messages.push(attachCodexMirrorIdentity(params.createAssistantMirrorMessage("Codex reasoning", params.reasoningText), `${params.turnId}:reasoning`));
939
- if (params.planText) messages.push(attachCodexMirrorIdentity(params.createAssistantMirrorMessage("Codex plan", params.planText), `${params.turnId}:plan`));
940
- const visibleWorkMessages = [...params.runParams.config?.ui?.prefs?.chatPersistCommentary === false ? [] : params.commentaryMessages.map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:commentary:${itemId}`)), ...params.toolMessages].toSorted((left, right) => (asDateTimestampMs(left.timestamp) ?? 0) - (asDateTimestampMs(right.timestamp) ?? 0));
1035
+ const commentaryMessages = params.runParams.config?.ui?.prefs?.chatPersistCommentary === false ? [] : params.commentaryMessages.map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:commentary:${itemId}`));
1036
+ const asyncMessages = params.asyncMessages.map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:async:${itemId}`));
1037
+ const visibleWorkMessages = [
1038
+ ...commentaryMessages,
1039
+ ...asyncMessages,
1040
+ ...(params.assistantMessages ?? []).map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:assistant:${itemId}`)),
1041
+ ...params.toolMessages
1042
+ ].toSorted((left, right) => (asDateTimestampMs(left.timestamp) ?? 0) - (asDateTimestampMs(right.timestamp) ?? 0));
941
1043
  messages.push(...visibleWorkMessages);
942
1044
  if (params.lastAssistant) messages.push(attachCodexMirrorIdentity(params.lastAssistant, `${params.turnId}:assistant`));
943
- return applyStickyTurnTaint(messages).map((message) => projectAgentHarnessTranscriptMessageForDisplay({
1045
+ const taint = { tainted: false };
1046
+ return messages.map((message) => projectAgentHarnessTranscriptMessageForDisplay({
944
1047
  hidden: params.runParams.trigger === "memory",
945
- message
1048
+ message: applyCodexTranscriptTaint(message, taint)
946
1049
  }));
947
1050
  }
1051
+ function buildCodexSteeringMessagesSnapshot(params) {
1052
+ const asyncMessages = params.assistantProjection.collectAsyncMessages().filter(({ itemId }) => params.completedItemIds.has(itemId));
1053
+ const commentaryMessages = params.assistantProjection.collectCommentaryMessages().filter(({ itemId }) => params.completedItemIds.has(itemId));
1054
+ const assistantMessages = params.assistantProjection.collectCompletedAssistantMessages(params.completedItemIds, {
1055
+ tokenUsage: void 0,
1056
+ aborted: false,
1057
+ promptError: void 0
1058
+ });
1059
+ return {
1060
+ messages: buildCodexMessagesSnapshot({
1061
+ runParams: params.runParams,
1062
+ turnId: params.turnId,
1063
+ upstreamUserText: params.upstreamUserText,
1064
+ reasoningText: void 0,
1065
+ asyncMessages,
1066
+ commentaryMessages,
1067
+ assistantMessages,
1068
+ toolMessages: params.toolMessages,
1069
+ lastAssistant: void 0,
1070
+ createAssistantMirrorMessage: (title, text) => params.assistantProjection.createAssistantMirrorMessage(title, text)
1071
+ }).filter((message) => message.role !== "user"),
1072
+ assistantBoundaryItemId: assistantMessages.at(-1)?.itemId
1073
+ };
1074
+ }
948
1075
  //#endregion
949
1076
  //#region extensions/codex/src/app-server/event-projector-result.ts
950
1077
  function buildCodexAttemptResult(input) {
951
1078
  input.nativeToolLifecycleProjection.finalizeActive();
952
1079
  const assistantTexts = input.assistantProjection.collectAssistantTexts();
1080
+ const asyncMessages = input.assistantProjection.collectAsyncMessages();
953
1081
  const commentaryMessages = input.assistantProjection.collectCommentaryMessages();
954
1082
  const reasoningText = input.reasoningProjection.reasoningText();
955
1083
  const planText = input.reasoningProjection.planText();
@@ -984,7 +1112,7 @@ function buildCodexAttemptResult(input) {
984
1112
  turnId: input.turnId,
985
1113
  upstreamUserText: input.upstreamUserText,
986
1114
  reasoningText,
987
- planText,
1115
+ asyncMessages,
988
1116
  commentaryMessages,
989
1117
  toolMessages: input.toolTranscriptProjection.transcriptMessages,
990
1118
  lastAssistant,
@@ -1001,7 +1129,9 @@ function buildCodexAttemptResult(input) {
1001
1129
  });
1002
1130
  const toolMetas = input.toolProgressProjection.toolMetas;
1003
1131
  const hadPotentialSideEffects = input.toolTelemetry.didSendViaMessagingTool || Boolean(input.toolTelemetry.successfulCronAdds || input.toolTelemetry.acceptedSessionSpawns?.length) || input.generatedMediaProjection.hasGeneratedMedia() || input.toolProgressProjection.hasPotentialSideEffects;
1004
- return {
1132
+ const sentMediaUrls = new Set(input.toolTelemetry.messagingToolSentMediaUrls.map((url) => url.trim()));
1133
+ const toolAutoDeliveryMediaUrls = input.toolTelemetry.toolAutoDeliveryMediaUrls?.filter((url) => !sentMediaUrls.has(url.trim()));
1134
+ const result = {
1005
1135
  terminal: attemptTerminal.normalize({
1006
1136
  aborted: input.aborted,
1007
1137
  promptError,
@@ -1048,8 +1178,83 @@ function buildCodexAttemptResult(input) {
1048
1178
  yieldDetected: input.yieldDetected || false,
1049
1179
  didSendDeterministicApprovalPrompt: input.guardianReviewCount > 0 ? false : void 0
1050
1180
  };
1181
+ const transferTtsProvenance = resolveCodexTtsProvenanceTransfer(input.runParams.hostCapabilities);
1182
+ for (const toolResult of input.toolTelemetry.coreTtsToolResults ?? []) transferTtsProvenance?.(toolResult, result, toolAutoDeliveryMediaUrls ?? []);
1183
+ return result;
1051
1184
  }
1052
1185
  //#endregion
1186
+ //#region extensions/codex/src/app-server/transcript-checkpoint.ts
1187
+ /** Commits completed work in receipt order without waiting for the enclosing turn. */
1188
+ var CodexTranscriptCheckpoint = class {
1189
+ constructor(params, threadId, turnId) {
1190
+ this.params = params;
1191
+ this.threadId = threadId;
1192
+ this.turnId = turnId;
1193
+ this.pending = [];
1194
+ this.commentaryItemIds = /* @__PURE__ */ new Set();
1195
+ this.lastTimestamp = 0;
1196
+ this.writing = Promise.resolve();
1197
+ this.tainted = false;
1198
+ this.closed = false;
1199
+ this.nextTimestamp = () => {
1200
+ this.lastTimestamp = Math.max(Date.now(), this.lastTimestamp + 1);
1201
+ return this.lastTimestamp;
1202
+ };
1203
+ this.enqueueCommentary = (itemId, entry) => {
1204
+ if (this.params.config?.ui?.prefs?.chatPersistCommentary === false || this.commentaryItemIds.has(itemId)) return;
1205
+ this.commentaryItemIds.add(itemId);
1206
+ this.enqueue({
1207
+ ...entry,
1208
+ read: () => {
1209
+ const message = entry.read();
1210
+ return message ? attachCodexMirrorIdentity(message, `${this.turnId}:commentary:${itemId}`) : void 0;
1211
+ }
1212
+ });
1213
+ };
1214
+ this.enqueue = (entry) => {
1215
+ if (this.params.sessionTarget && !this.closed) this.pending.push(entry);
1216
+ };
1217
+ }
1218
+ flush(close = false) {
1219
+ if (this.closed) return this.writing;
1220
+ this.closed = close;
1221
+ this.writing = this.writing.then(async () => {
1222
+ const blocked = close ? -1 : this.pending.findIndex((entry) => entry.ready?.() === false);
1223
+ const count = blocked < 0 ? this.pending.length : blocked;
1224
+ if (count === 0) return;
1225
+ const taint = { tainted: this.tainted };
1226
+ const messages = this.pending.slice(0, count).flatMap((entry) => {
1227
+ const message = entry.read();
1228
+ return message ? [projectAgentHarnessTranscriptMessageForDisplay({
1229
+ hidden: this.params.trigger === "memory",
1230
+ message: applyCodexTranscriptTaint(message, taint)
1231
+ })] : [];
1232
+ });
1233
+ try {
1234
+ await codexTranscriptMirrorRuntime.mirror({
1235
+ ...this.params.sessionTarget,
1236
+ sessionId: this.params.sessionId,
1237
+ cwd: this.params.workspaceDir,
1238
+ messages,
1239
+ idempotencyScope: `codex-app-server:${this.threadId}`,
1240
+ runId: this.params.runId,
1241
+ runMirrorIdentityPrefix: `${this.turnId}:`,
1242
+ config: this.params.config
1243
+ });
1244
+ this.pending.splice(0, count);
1245
+ this.tainted = taint.tainted;
1246
+ } catch (error) {
1247
+ embeddedAgentLog.warn("failed to checkpoint codex app-server transcript", {
1248
+ runId: this.params.runId,
1249
+ sessionId: this.params.sessionId,
1250
+ error: formatErrorMessage(error)
1251
+ });
1252
+ }
1253
+ });
1254
+ return this.writing;
1255
+ }
1256
+ };
1257
+ //#endregion
1053
1258
  //#region extensions/codex/src/app-server/usage-limit-error.ts
1054
1259
  /**
1055
1260
  * Enriches Codex usage-limit failures with current rate-limit information and
@@ -1204,7 +1409,8 @@ var CodexAppServerEventProjector = class {
1204
1409
  this.aborted = false;
1205
1410
  this.responseCompletions = new CodexResponseCompletionProjection();
1206
1411
  this.completedCompactionCount = 0;
1207
- this.lastTranscriptTimestamp = 0;
1412
+ this.transcriptCheckpoint = new CodexTranscriptCheckpoint(params, threadId, turnId);
1413
+ this.asyncDeliveryProjection = new CodexAsyncDeliveryProjection(params, threadId, turnId, options);
1208
1414
  this.contextTokens = options.initialContextTokens;
1209
1415
  this.contextTokensSource = options.initialContextTokens === void 0 ? void 0 : "resolved";
1210
1416
  this.diagnostics = new CodexProjectionDiagnostics(threadId, turnId);
@@ -1216,35 +1422,37 @@ var CodexAppServerEventProjector = class {
1216
1422
  signal: options.runAbortSignal
1217
1423
  });
1218
1424
  this.toolProgressProjection = new CodexToolProgressProjection(params);
1219
- this.toolTranscriptProjection = new CodexToolTranscriptProjection(params, threadId, turnId, this.toolProgressProjection, () => this.nextTranscriptTimestamp(), {
1425
+ this.toolTranscriptProjection = new CodexToolTranscriptProjection(params, threadId, turnId, this.toolProgressProjection, this.transcriptCheckpoint.nextTimestamp, {
1220
1426
  nativePostToolUseRelayEnabled: options.nativePostToolUseRelayEnabled,
1221
1427
  prepareNativeMcpAppResultDetails: options.prepareNativeMcpAppResultDetails,
1222
- trajectoryRecorder: options.trajectoryRecorder
1428
+ trajectoryRecorder: options.trajectoryRecorder,
1429
+ checkpointMessage: this.transcriptCheckpoint.enqueue
1223
1430
  });
1224
1431
  this.eventProjection = new CodexEventProjection(threadId, turnId, (event) => this.emitAgentEvent(event), this.toolProgressProjection, this.toolTranscriptProjection, options.onNativeToolResultRecorded);
1225
- this.assistantProjection = new CodexAssistantProjection(params, (event) => this.emitAgentEvent(event), (text) => this.toolProgressProjection.matchesEcho(text), () => this.nextTranscriptTimestamp());
1432
+ this.assistantProjection = new CodexAssistantProjection(params, (event) => this.emitAgentEvent(event), (text) => this.toolProgressProjection.matchesEcho(text), this.transcriptCheckpoint.nextTimestamp, this.transcriptCheckpoint.enqueueCommentary);
1226
1433
  this.reasoningProjection = new CodexReasoningProjection(params, (event) => this.emitAgentEvent(event), options.onNativePlanUpdate);
1227
1434
  }
1228
- nextTranscriptTimestamp() {
1229
- this.lastTranscriptTimestamp = Math.max(Date.now(), this.lastTranscriptTimestamp + 1);
1230
- return this.lastTranscriptTimestamp;
1231
- }
1232
1435
  getCompletedTurnStatus() {
1233
1436
  return this.completedTurn?.status;
1234
1437
  }
1235
1438
  buildSteeringTranscriptPrefix() {
1236
- const commentaryMessages = this.assistantProjection.collectCommentaryMessages().filter(({ itemId }) => this.completedItemIds.has(itemId));
1237
- return buildCodexMessagesSnapshot({
1439
+ const snapshot = buildCodexSteeringMessagesSnapshot({
1238
1440
  runParams: this.params,
1239
1441
  turnId: this.turnId,
1240
1442
  upstreamUserText: this.options.upstreamUserText,
1241
- reasoningText: void 0,
1242
- planText: void 0,
1243
- commentaryMessages,
1244
- toolMessages: this.toolTranscriptProjection.transcriptMessages,
1245
- lastAssistant: void 0,
1246
- createAssistantMirrorMessage: (title, text) => this.assistantProjection.createAssistantMirrorMessage(title, text)
1247
- }).filter((message) => message.role !== "user");
1443
+ completedItemIds: this.completedItemIds,
1444
+ assistantProjection: this.assistantProjection,
1445
+ toolMessages: this.toolTranscriptProjection.transcriptMessages
1446
+ });
1447
+ this.pendingSteeringAssistantBoundaryItemId = snapshot.assistantBoundaryItemId;
1448
+ return snapshot.messages;
1449
+ }
1450
+ markSteeringTranscriptPersisted() {
1451
+ const itemId = this.pendingSteeringAssistantBoundaryItemId;
1452
+ if (itemId) {
1453
+ this.assistantProjection.markAssistantBoundaryPersisted(itemId);
1454
+ this.pendingSteeringAssistantBoundaryItemId = void 0;
1455
+ }
1248
1456
  }
1249
1457
  hasCompletedTerminalAssistantText() {
1250
1458
  return this.assistantProjection.hasCompletedTerminalAssistantText(this.completedItemIds);
@@ -1284,8 +1492,9 @@ var CodexAppServerEventProjector = class {
1284
1492
  if (!params) return;
1285
1493
  if (notification.method === "hook/started" || notification.method === "hook/completed") {
1286
1494
  if (!this.isHookNotificationForCurrentThread(params)) return;
1287
- } else if (notification.method === "guardianWarning") {
1288
- if (readCodexNotificationThreadId(params) !== this.threadId) return;
1495
+ } else if (notification.method === "guardianWarning" || notification.method === "warning" || notification.method === "configWarning") {
1496
+ const warningThreadId = readCodexNotificationThreadId(params);
1497
+ if (notification.method === "guardianWarning" && warningThreadId !== this.threadId || warningThreadId && warningThreadId !== this.threadId) return;
1289
1498
  } else if (!isCodexNotificationForTurn(params, this.threadId, this.turnId)) return;
1290
1499
  if (notification.method !== "guardianWarning" && notification.method !== "item/autoApprovalReview/started" && notification.method !== "item/autoApprovalReview/completed") this.eventProjection.flushPendingGuardianWarning();
1291
1500
  this.nativeToolLifecycleProjector.handleNotification(notification);
@@ -1306,9 +1515,11 @@ var CodexAppServerEventProjector = class {
1306
1515
  break;
1307
1516
  case "item/started":
1308
1517
  await this.handleItemStarted(params);
1518
+ await this.transcriptCheckpoint.flush();
1309
1519
  break;
1310
1520
  case "item/completed":
1311
1521
  await this.handleItemCompleted(params);
1522
+ await this.transcriptCheckpoint.flush();
1312
1523
  break;
1313
1524
  case "item/commandExecution/outputDelta":
1314
1525
  this.toolProgressProjection.handleOutputDelta(params, "bash");
@@ -1317,9 +1528,16 @@ var CodexAppServerEventProjector = class {
1317
1528
  case "item/autoApprovalReview/completed":
1318
1529
  this.eventProjection.handleGuardianReview(notification.method, params);
1319
1530
  break;
1531
+ case "autoApprovalReview/strictReviewRequired":
1532
+ this.eventProjection.handleStrictReviewRequired(params);
1533
+ break;
1320
1534
  case "guardianWarning":
1321
1535
  this.eventProjection.handleGuardianWarning(params);
1322
1536
  break;
1537
+ case "warning":
1538
+ case "configWarning":
1539
+ this.eventProjection.handleWarning(params);
1540
+ break;
1323
1541
  case "hook/started":
1324
1542
  case "hook/completed":
1325
1543
  this.eventProjection.handleHook(notification.method, params);
@@ -1331,7 +1549,7 @@ var CodexAppServerEventProjector = class {
1331
1549
  this.contextTokensSource = this.params.authoredContextTokenCap === void 0 ? "runtime" : "runtime-configured";
1332
1550
  }
1333
1551
  this.emitAgentEvent({
1334
- stream: "codex_app_server.usage",
1552
+ stream: "usage",
1335
1553
  data
1336
1554
  });
1337
1555
  });
@@ -1340,10 +1558,14 @@ var CodexAppServerEventProjector = class {
1340
1558
  await this.handleTurnCompleted(params);
1341
1559
  break;
1342
1560
  case "rawResponse/completed":
1343
- this.responseCompletions.record(params);
1561
+ this.responseCompletions.record(params, this.params.hostCapabilities.reportOutputTokens);
1344
1562
  break;
1345
1563
  case "rawResponseItem/completed":
1346
1564
  await this.handleRawResponseItemCompleted(params);
1565
+ await this.transcriptCheckpoint.flush();
1566
+ break;
1567
+ case "model/rerouted":
1568
+ this.eventProjection.handleModelRerouted(params);
1347
1569
  break;
1348
1570
  case "error": {
1349
1571
  this.responseCompletions.clear();
@@ -1363,13 +1585,10 @@ var CodexAppServerEventProjector = class {
1363
1585
  case "item/fileChange/outputDelta":
1364
1586
  case "item/fileChange/patchUpdated":
1365
1587
  case "item/mcpToolCall/progress":
1366
- case "model/rerouted":
1367
1588
  case "model/verification":
1368
1589
  case "turn/moderationMetadata":
1369
1590
  case "model/safetyBuffering/updated": break;
1370
- default:
1371
- this.diagnostics.warnUnknownEvent(notification, params);
1372
- break;
1591
+ default: this.diagnostics.warnUnknownEvent(notification, params);
1373
1592
  }
1374
1593
  }
1375
1594
  buildResult(toolTelemetry, options) {
@@ -1495,7 +1714,9 @@ var CodexAppServerEventProjector = class {
1495
1714
  this.activeItemIds.delete(itemId);
1496
1715
  this.completedItemIds.add(itemId);
1497
1716
  }
1498
- this.assistantProjection.recordItemCompleted(item, itemId, this.activeItemIds);
1717
+ if (!this.asyncDeliveryProjection.allows(item, true)) return;
1718
+ const asyncMessage = this.assistantProjection.recordItemCompleted(item, itemId, this.activeItemIds);
1719
+ if (asyncMessage) await this.asyncDeliveryProjection.deliver(asyncMessage);
1499
1720
  this.reasoningProjection.recordItem(item);
1500
1721
  await this.generatedMediaProjection.recordNative(item);
1501
1722
  if (item?.type === "contextCompaction" && itemId) {
@@ -1525,9 +1746,9 @@ var CodexAppServerEventProjector = class {
1525
1746
  threadId: this.threadId,
1526
1747
  turnId: this.turnId,
1527
1748
  itemId,
1528
- timestamp: this.nextTranscriptTimestamp()
1749
+ timestamp: this.transcriptCheckpoint.nextTimestamp()
1529
1750
  });
1530
- this.emitCompactionEnd(itemId, true);
1751
+ this.eventProjection.emitCompactionEnd(itemId, true);
1531
1752
  }
1532
1753
  this.toolProgressProjection.recordToolMeta(item);
1533
1754
  this.toolProgressProjection.rememberCommandAggregateOutputEcho(item);
@@ -1573,10 +1794,11 @@ var CodexAppServerEventProjector = class {
1573
1794
  for (const itemId of failedCompactionItemIds) {
1574
1795
  this.activeItemIds.delete(itemId);
1575
1796
  this.activeCompactionItemIds.delete(itemId);
1576
- this.emitCompactionEnd(itemId, false);
1797
+ this.eventProjection.emitCompactionEnd(itemId, false);
1577
1798
  }
1578
1799
  }
1579
1800
  const turnItems = turn.items ?? [];
1801
+ const unsettledAsyncDeliveries = this.asyncDeliveryProjection.pending();
1580
1802
  for (let index = turnItems.length - 1; index >= 0; index -= 1) {
1581
1803
  const item = turnItems[index];
1582
1804
  if (!item || !this.isCurrentTurnSnapshotItem(item)) continue;
@@ -1587,8 +1809,10 @@ var CodexAppServerEventProjector = class {
1587
1809
  }
1588
1810
  }
1589
1811
  for (const item of turnItems) {
1812
+ if (!this.asyncDeliveryProjection.allows(item)) continue;
1590
1813
  this.diagnostics.warnUnknownItemStatus(item);
1591
- this.assistantProjection.recordSnapshotItem(item);
1814
+ const asyncMessage = this.assistantProjection.recordSnapshotItem(item);
1815
+ if (asyncMessage) await this.asyncDeliveryProjection.deliver(asyncMessage);
1592
1816
  this.reasoningProjection.recordItem(item);
1593
1817
  await this.generatedMediaProjection.recordNative(item);
1594
1818
  this.toolProgressProjection.recordToolMeta(item);
@@ -1601,23 +1825,11 @@ var CodexAppServerEventProjector = class {
1601
1825
  this.toolProgressProjection.emitToolResultOutput(item);
1602
1826
  }
1603
1827
  this.toolProgressProjection.approvalTimeoutKinds.clear();
1828
+ for (const delivery of unsettledAsyncDeliveries) await this.asyncDeliveryProjection.deliver(delivery);
1604
1829
  this.assistantProjection.finalizeAnswerCandidate(turn);
1605
1830
  this.activeCompactionItemIds.clear();
1606
1831
  await this.reasoningProjection.maybeEndReasoning();
1607
1832
  }
1608
- emitCompactionEnd(itemId, completed) {
1609
- this.emitAgentEvent({
1610
- stream: "compaction",
1611
- data: {
1612
- phase: "end",
1613
- backend: "codex-app-server",
1614
- completed,
1615
- threadId: this.threadId,
1616
- turnId: this.turnId,
1617
- itemId
1618
- }
1619
- });
1620
- }
1621
1833
  async emitSnapshotOnlyNativeToolProgress(item) {
1622
1834
  if (!shouldSynthesizeToolProgressForItem(item) || !this.isCurrentTurnSnapshotItem(item) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
1623
1835
  if (!this.activeItemIds.has(item.id)) {
@@ -1675,24 +1887,7 @@ var CodexAppServerEventProjector = class {
1675
1887
  return usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : readCodexErrorNotificationMessage(params);
1676
1888
  }
1677
1889
  emitAgentEvent(event) {
1678
- try {
1679
- emitAgentEvent({
1680
- runId: this.params.runId,
1681
- stream: event.stream,
1682
- data: event.data,
1683
- ...this.params.sessionKey ? { sessionKey: this.params.sessionKey } : {}
1684
- });
1685
- } catch (error) {
1686
- embeddedAgentLog.debug("codex app-server global agent event emit failed", { error });
1687
- }
1688
- try {
1689
- const maybePromise = this.params.onAgentEvent?.(event);
1690
- Promise.resolve(maybePromise).catch((error) => {
1691
- embeddedAgentLog.debug("codex app-server agent event handler rejected", { error });
1692
- });
1693
- } catch (error) {
1694
- embeddedAgentLog.debug("codex app-server agent event handler threw", { error });
1695
- }
1890
+ emitCodexAgentEvent(this.params, event);
1696
1891
  }
1697
1892
  isHookNotificationForCurrentThread(params) {
1698
1893
  const threadId = readStringField(params, "threadId");
@@ -1701,11 +1896,30 @@ var CodexAppServerEventProjector = class {
1701
1896
  }
1702
1897
  };
1703
1898
  //#endregion
1899
+ //#region extensions/codex/src/app-server/mcp-tool-metadata.ts
1900
+ /** Hosted app ownership is authoritative only on metadata supplied by Codex. */
1901
+ function readCodexMcpToolConnectorId(tool) {
1902
+ const metadata = asOptionalRecord(asOptionalRecord(tool)?.["_meta"]);
1903
+ return normalizeOptionalString(metadata?.connector_id) ?? normalizeOptionalString(metadata?.connectorId);
1904
+ }
1905
+ /** Preserve MCP App visibility so model-only tools cannot become widget authority. */
1906
+ function readCodexMcpToolUiVisibility(tool) {
1907
+ const metadata = asOptionalRecord(asOptionalRecord(tool)?.["_meta"]);
1908
+ const visibility = asOptionalRecord(metadata?.ui)?.visibility;
1909
+ if (!Array.isArray(visibility)) return;
1910
+ return [...new Set(visibility.filter((value) => value === "app" || value === "model"))].toSorted();
1911
+ }
1912
+ //#endregion
1704
1913
  //#region extensions/codex/src/app-server/native-mcp-app.ts
1914
+ const CODEX_APPS_MCP_SERVER$1 = "codex_apps";
1705
1915
  function readMcpAppResourceUri(item) {
1706
- const uri = normalizeOptionalString(asOptionalRecord(item.appContext)?.resourceUri) ?? normalizeOptionalString(item.mcpAppResourceUri);
1916
+ const appContext = asOptionalRecord(item.appContext);
1917
+ const uri = normalizeOptionalString(appContext?.resourceUri) ?? normalizeOptionalString(item.mcpAppResourceUri);
1707
1918
  return uri?.startsWith("ui://") ? uri : void 0;
1708
1919
  }
1920
+ function readMcpAppConnectorId(item) {
1921
+ return normalizeOptionalString(asOptionalRecord(item.appContext)?.connectorId);
1922
+ }
1709
1923
  function readMcpToolResult(item) {
1710
1924
  const result = asOptionalRecord(item.result);
1711
1925
  if (!result || !Array.isArray(result.content)) return;
@@ -1743,13 +1957,16 @@ function createNativeMcpRuntime(params) {
1743
1957
  launchSummary: "Codex native MCP connection",
1744
1958
  toolCount: Object.keys(status.tools).length
1745
1959
  }])),
1746
- tools: loaded.flatMap((status) => statusTools(status).map((tool) => ({
1747
- serverName: status.name,
1748
- safeServerName: status.name,
1749
- toolName: String(tool.name),
1750
- inputSchema: asOptionalRecord(tool.inputSchema) ?? { type: "object" },
1751
- fallbackDescription: normalizeOptionalString(tool.description) ?? String(tool.name)
1752
- })))
1960
+ tools: loaded.flatMap((status) => statusTools(status).map((tool) => {
1961
+ const uiVisibility = readCodexMcpToolUiVisibility(tool);
1962
+ return Object.assign({
1963
+ serverName: status.name,
1964
+ safeServerName: status.name,
1965
+ toolName: String(tool.name),
1966
+ inputSchema: asOptionalRecord(tool.inputSchema) ?? { type: "object" },
1967
+ fallbackDescription: normalizeOptionalString(tool.description) ?? String(tool.name)
1968
+ }, uiVisibility ? { uiVisibility } : {});
1969
+ }))
1753
1970
  };
1754
1971
  return catalog;
1755
1972
  };
@@ -1777,11 +1994,18 @@ function createNativeMcpRuntime(params) {
1777
1994
  const status = (await loadStatuses()).find((entry) => entry.name === serverName);
1778
1995
  return { tools: status ? statusTools(status) : [] };
1779
1996
  },
1780
- readResource: async (serverName, uri) => await params.client.request("mcpServer/resource/read", {
1781
- threadId: params.threadId,
1782
- server: serverName,
1783
- uri
1784
- }),
1997
+ readResource: async (serverName, uri) => {
1998
+ const isCodexAppsServer = serverName === CODEX_APPS_MCP_SERVER$1;
1999
+ const response = await params.client.request("mcpServer/resource/read", {
2000
+ threadId: params.threadId,
2001
+ ...isCodexAppsServer ? { originCallId: params.originCallId } : {},
2002
+ server: serverName,
2003
+ uri,
2004
+ ...params.connectorId ? { connectorId: params.connectorId } : {}
2005
+ });
2006
+ if (isCodexAppsServer && response.originCallId !== params.originCallId) throw new Error(`Codex MCP resource response originCallId mismatch: expected ${params.originCallId}, received ${response.originCallId}`);
2007
+ return response;
2008
+ },
1785
2009
  listResources: async (serverName) => {
1786
2010
  return { resources: (await loadStatuses()).find((entry) => entry.name === serverName)?.resources ?? [] };
1787
2011
  },
@@ -1794,15 +2018,25 @@ function createNativeMcpRuntime(params) {
1794
2018
  }
1795
2019
  function createCodexNativeMcpAppResultDetailsPreparer(params) {
1796
2020
  if (params.attempt.config?.mcp?.apps?.enabled !== true) return;
1797
- const runtime = createNativeMcpRuntime(params);
1798
2021
  return async (item) => {
1799
2022
  const serverName = normalizeOptionalString(item.server);
1800
2023
  const toolName = normalizeOptionalString(item.tool);
1801
2024
  const uiResourceUri = readMcpAppResourceUri(item);
2025
+ const connectorId = readMcpAppConnectorId(item);
1802
2026
  const toolResult = readMcpToolResult(item);
1803
2027
  if (!serverName || !toolName || !uiResourceUri || !toolResult) return;
1804
- const allowedAppToolNames = new Set((await runtime.getCatalog()).tools.filter((tool) => tool.serverName === serverName).map((tool) => tool.toolName));
1805
- if (allowedAppToolNames.size === 0) return;
2028
+ if (serverName === CODEX_APPS_MCP_SERVER$1 && !connectorId) return;
2029
+ const runtime = createNativeMcpRuntime({
2030
+ ...params,
2031
+ originCallId: item.id,
2032
+ ...connectorId ? { connectorId } : {}
2033
+ });
2034
+ const tools = (await runtime.listTools?.(serverName))?.tools ?? [];
2035
+ const allowedAppToolNames = new Set(tools.filter((tool) => {
2036
+ const uiVisibility = readCodexMcpToolUiVisibility(tool);
2037
+ return (uiVisibility === void 0 || uiVisibility.includes("app")) && (serverName !== CODEX_APPS_MCP_SERVER$1 || readCodexMcpToolConnectorId(tool) === connectorId);
2038
+ }).map((tool) => tool.name));
2039
+ if (!allowedAppToolNames.has(toolName)) return;
1806
2040
  return await prepareHarnessNativeMcpAppPreview({
1807
2041
  runtime,
1808
2042
  serverName,
@@ -1821,9 +2055,9 @@ function createCodexNativeMcpAppResultDetailsPreparer(params) {
1821
2055
  const RESTORED_PLAN_PREAMBLE = "OpenClaw restored the session progress card after context compaction. This is application state, not a new user request. Continue the current task and call progress_card whenever the status meaningfully changes.";
1822
2056
  const PROGRESS_CARD_MAX_STEPS = 50;
1823
2057
  const PROGRESS_CARD_MAX_STEP_UTF8_BYTES = 512;
1824
- const PROGRESS_CARD_MAX_UTF8_BYTES = 8 * 1024;
1825
- const RESTORED_PLAN_MAX_MARKDOWN_BYTES = 2 * 1024;
1826
- const RESTORED_PLAN_MAX_PAYLOAD_BYTES = 32 * 1024;
2058
+ const PROGRESS_CARD_MAX_UTF8_BYTES = 8192;
2059
+ const RESTORED_PLAN_MAX_MARKDOWN_BYTES = 2048;
2060
+ const RESTORED_PLAN_MAX_PAYLOAD_BYTES = 32768;
1827
2061
  const RESTORED_PLAN_TRUNCATION_SUFFIX = "…";
1828
2062
  function canonicalizeNativeProgressCardInput(input) {
1829
2063
  const markdown = input.markdown === void 0 ? void 0 : truncateUtf8(stripInvisibleUnicode(input.markdown), PROGRESS_CARD_MAX_UTF8_BYTES);
@@ -1833,8 +2067,10 @@ function canonicalizeNativeProgressCardInput(input) {
1833
2067
  const text = truncateUtf8(stripInvisibleUnicode(step.step), PROGRESS_CARD_MAX_STEP_UTF8_BYTES);
1834
2068
  if (!text.trim()) continue;
1835
2069
  let status = step.status;
1836
- if (status === "in_progress") if (hasInProgressStep) status = "pending";
1837
- else hasInProgressStep = true;
2070
+ if (status === "in_progress") {
2071
+ if (hasInProgressStep) status = "pending";
2072
+ else hasInProgressStep = true;
2073
+ }
1838
2074
  plan.push({
1839
2075
  step: text,
1840
2076
  status
@@ -1903,7 +2139,8 @@ function serializePlan(plan) {
1903
2139
  }
1904
2140
  function truncateUtf8(value, maxBytes) {
1905
2141
  if (Buffer$1.byteLength(value, "utf8") <= maxBytes) return value;
1906
- return `${truncateUtf8Prefix(value, maxBytes - Buffer$1.byteLength(RESTORED_PLAN_TRUNCATION_SUFFIX, "utf8"))}${RESTORED_PLAN_TRUNCATION_SUFFIX}`;
2142
+ const suffixBytes = Buffer$1.byteLength(RESTORED_PLAN_TRUNCATION_SUFFIX, "utf8");
2143
+ return `${truncateUtf8Prefix(value, maxBytes - suffixBytes)}${RESTORED_PLAN_TRUNCATION_SUFFIX}`;
1907
2144
  }
1908
2145
  function readPlanSteps(value) {
1909
2146
  if (!Array.isArray(value)) return [];
@@ -1923,7 +2160,7 @@ function isPlanStatus(value) {
1923
2160
  //#endregion
1924
2161
  //#region extensions/codex/src/app-server/user-input-bridge.ts
1925
2162
  /** Owns per-turn Codex request_user_input and ordinary MCP elicitation lifecycles. */
1926
- const DEFAULT_USER_INPUT_TIMEOUT_MS = 15 * 6e4;
2163
+ const DEFAULT_USER_INPUT_TIMEOUT_MS = 9e5;
1927
2164
  const NONBLOCKING_USER_INPUT_TIMEOUT_MS = 12e4;
1928
2165
  const MAX_USER_INPUT_QUESTIONS = 3;
1929
2166
  const MAX_USER_INPUT_OPTIONS = 4;
@@ -2047,7 +2284,10 @@ function createCodexUserInputBridge(params) {
2047
2284
  failureValue: declineElicitation("OpenClaw could not handle this elicitation."),
2048
2285
  run: async (signal) => {
2049
2286
  const result = await execute(compiled.input, timeoutMs, signal);
2050
- if (result.status === "answered") return createCodexElicitationResponse("accept", compiled.input.kind === "ready" && compiled.input.plan.kind === "url" ? null : result.content);
2287
+ if (result.status === "answered") {
2288
+ const content = compiled.input.kind === "ready" && compiled.input.plan.kind === "url" ? null : result.content;
2289
+ return createCodexElicitationResponse("accept", content);
2290
+ }
2051
2291
  if (result.status === "declined") return declineElicitation(result.message);
2052
2292
  if (result.status === "unsupported") return declineElicitation(result.message);
2053
2293
  return cancelValue;
@@ -2166,7 +2406,7 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2166
2406
  const { context, turnState } = prompt;
2167
2407
  const { runtime, attemptTools } = context;
2168
2408
  const { connection } = runtime;
2169
- const { params, runAbortController, terminalState, abortExplicitly, abortFromUpstream, bindingStore, bindingIdentity, sessionAgentId, sandboxSessionKey, contextSessionKey, effectiveCwd } = connection;
2409
+ const { params, runAbortController, terminalState, abortExplicitly, abortFromUpstream, bindingStore, bindingIdentity, sessionAgentId, contextSessionKey, effectiveCwd } = connection;
2170
2410
  const { dynamicToolParams, compactionPlanState, computerContextEpoch, toolBridge } = attemptTools;
2171
2411
  const { state, userInputBridgeRef, steeringQueueRef, turnWatches, completeTurn, interruptTurn } = turnRuntime;
2172
2412
  const { emitExecutionPhaseOnce, emitLifecycleStart, maybeAnnounceFastModeAutoOff } = lifecycle;
@@ -2208,6 +2448,16 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2208
2448
  }, resourceState.thread.threadId, activeTurnId, {
2209
2449
  initialContextTokens: connection.mutable.startupContextTokens,
2210
2450
  nativePostToolUseRelayEnabled: resourceState.nativeHookRelay?.allowedEvents.includes("post_tool_use") === true && resourceState.nativeHookRelay.shouldRelayEvent("post_tool_use"),
2451
+ asyncUserMessageAllowed: params.disableTools !== true && (params.toolsAllow === void 0 || toolBridge.availableTools.some((tool) => tool.name === "message")),
2452
+ onAsyncDelivery: async (delivery) => {
2453
+ return await codexTranscriptMirrorRuntime.deliverAsyncMessageBestEffort({
2454
+ params: dynamicToolParams,
2455
+ cwd: effectiveCwd,
2456
+ threadId: resourceState.thread.threadId,
2457
+ turnId: activeTurnId,
2458
+ ...delivery
2459
+ });
2460
+ },
2211
2461
  readRecentRateLimits: () => readRecentCodexRateLimits(resourceState.client),
2212
2462
  runAbortSignal: runAbortController.signal,
2213
2463
  remoteWorkspaceRoot: connection.appServer.remoteWorkspaceRoot,
@@ -2265,6 +2515,18 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2265
2515
  turnWatches.armAttemptIdleWatch();
2266
2516
  turnWatches.touchActivity("turn:start", { attemptProgress: true });
2267
2517
  for (const failure of pendingNativePreToolUseFailures.splice(0)) activeProjector.recordNativeToolPreToolUseFailure(failure);
2518
+ const notifyUserMessagePersisted = createCodexAppServerUserMessagePersistenceNotifier(params);
2519
+ const promptMirrorPromise = mirrorPromptAtTurnStartBestEffort({
2520
+ params,
2521
+ agentId: sessionAgentId,
2522
+ notifyUserMessagePersisted,
2523
+ sessionKey: contextSessionKey,
2524
+ cwd: effectiveCwd,
2525
+ threadId: resourceState.thread.threadId,
2526
+ turnId: activeTurnId,
2527
+ upstreamUserText: turnState.codexTurnPromptText
2528
+ });
2529
+ await promptMirrorPromise;
2268
2530
  if (resourceState.turnRoute) try {
2269
2531
  await resourceState.turnRoute.bindTurn(activeTurnId);
2270
2532
  } catch (error) {
@@ -2289,41 +2551,66 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2289
2551
  turnId: activeTurnId
2290
2552
  });
2291
2553
  }
2292
- const notifyUserMessagePersisted = createCodexAppServerUserMessagePersistenceNotifier(params);
2293
- const promptMirrorPromise = mirrorPromptAtTurnStartBestEffort({
2294
- params,
2295
- agentId: sessionAgentId,
2296
- notifyUserMessagePersisted,
2297
- sessionKey: sandboxSessionKey,
2298
- cwd: effectiveCwd,
2299
- threadId: resourceState.thread.threadId,
2300
- turnId: activeTurnId,
2301
- upstreamUserText: turnState.codexTurnPromptText
2554
+ const assertSteeringActive = () => {
2555
+ params.hostCapabilities.assertActive();
2556
+ runAbortController.signal.throwIfAborted();
2557
+ if (state.completed || state.terminalTurnNotificationQueued || state.timedOut) throw new Error("codex app-server turn is no longer accepting steering");
2558
+ };
2559
+ const workspaceOnly = resolveAttemptFsWorkspaceOnly({
2560
+ config: params.config,
2561
+ sessionAgentId
2302
2562
  });
2563
+ const imageContext = {
2564
+ workspaceDir: connection.effectiveWorkspace,
2565
+ model: params.model,
2566
+ config: params.config,
2567
+ workspaceOnly,
2568
+ localRoots: workspaceOnly ? void 0 : getAgentScopedMediaLocalRoots(params.config ?? {}, sessionAgentId),
2569
+ sandbox: connection.sandbox?.enabled && connection.sandbox.fsBridge ? {
2570
+ root: connection.sandbox.workspaceDir,
2571
+ bridge: connection.sandbox.fsBridge
2572
+ } : void 0
2573
+ };
2303
2574
  const activeSteeringQueue = createCodexSteeringQueue({
2304
2575
  client: resourceState.client,
2305
2576
  threadId: resourceState.thread.threadId,
2306
2577
  turnId: activeTurnId,
2307
2578
  requestTimeoutMs: connection.appServer.requestTimeoutMs,
2308
2579
  signal: runAbortController.signal,
2580
+ assertActive: assertSteeringActive,
2581
+ prepareMessage: async (text, options) => {
2582
+ const result = await detectAndLoadAgentHarnessPromptImages({
2583
+ ...imageContext,
2584
+ prompt: text,
2585
+ existingImages: options.images,
2586
+ imageOrder: options.imageOrder,
2587
+ media: options.media,
2588
+ userTurnTranscriptRecorder: options.userTurnTranscriptRecorder
2589
+ });
2590
+ if (result.failedMediaCount) throw new Error(`failed to hydrate ${result.failedMediaCount} structured image attachment(s) for Codex steering`);
2591
+ return buildCodexUserInput(text, result.images);
2592
+ },
2309
2593
  beforeConfirmConsumed: async (items) => {
2310
- const inboundItems = items.filter((item) => item.isInboundUserMessage === true);
2311
- if (inboundItems.length === 0) return;
2594
+ const transcriptItems = items.filter((item) => item.isInboundUserMessage === true || item.userTurnTranscriptRecorder !== void 0);
2595
+ if (transcriptItems.length === 0) return;
2312
2596
  await promptMirrorPromise;
2313
2597
  const messages = activeProjector.buildSteeringTranscriptPrefix();
2314
- if (params.sessionTarget && messages.length > 0) await codexTranscriptMirrorRuntime.mirror({
2315
- agentId: sessionAgentId,
2316
- sessionKey: contextSessionKey,
2317
- sessionId: params.sessionId,
2318
- storePath: params.sessionTarget.storePath,
2319
- cwd: effectiveCwd,
2320
- messages,
2321
- idempotencyScope: `codex-app-server:${resourceState.thread.threadId}`,
2322
- runId: params.runId,
2323
- runMirrorIdentityPrefix: `${activeTurnId}:`,
2324
- config: params.config
2325
- });
2326
- for (const item of inboundItems) {
2598
+ if (params.sessionTarget && messages.length > 0) {
2599
+ await codexTranscriptMirrorRuntime.mirror({
2600
+ agentId: sessionAgentId,
2601
+ sessionKey: contextSessionKey,
2602
+ sessionId: params.sessionId,
2603
+ storePath: params.sessionTarget.storePath,
2604
+ cwd: effectiveCwd,
2605
+ messages,
2606
+ idempotencyScope: `codex-app-server:${resourceState.thread.threadId}`,
2607
+ runId: params.runId,
2608
+ runMirrorIdentityPrefix: `${activeTurnId}:`,
2609
+ config: params.config
2610
+ });
2611
+ activeProjector.markSteeringTranscriptPersisted();
2612
+ }
2613
+ for (const item of transcriptItems) {
2327
2614
  const recorder = item.userTurnTranscriptRecorder;
2328
2615
  if (!recorder) continue;
2329
2616
  await recorder.persistApproved();
@@ -2333,7 +2620,8 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2333
2620
  });
2334
2621
  steeringQueueRef.current = activeSteeringQueue;
2335
2622
  const claimPendingUserInputAnswer = async (text, optionsLocal) => {
2336
- if (optionsLocal?.isInboundUserMessage !== true || optionsLocal.images?.length) return false;
2623
+ if (optionsLocal?.isInboundUserMessage !== true || hasPromptImageInput(optionsLocal)) return false;
2624
+ assertSteeringActive();
2337
2625
  return await claimPendingAgentQuestionAnswer({
2338
2626
  sessionKey: params.sessionKey ?? params.sessionId,
2339
2627
  text,
@@ -2347,14 +2635,17 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2347
2635
  resolvedBy
2348
2636
  });
2349
2637
  const queueMessage = async (text, optionsLocal) => {
2350
- const isInboundUserMessage = optionsLocal?.isInboundUserMessage === true;
2351
2638
  if (await claimPendingUserInputAnswer(text, optionsLocal)) {
2352
2639
  optionsLocal?.onQueueAccepted?.(true);
2353
2640
  return;
2354
- } else if (isInboundUserMessage && optionsLocal?.images?.length) try {
2355
- await cancelPendingUserInput("image-reply");
2356
- } catch (error) {
2357
- embeddedAgentLog.warn("failed to cancel codex gateway question before image steering", { error });
2641
+ }
2642
+ if (optionsLocal?.isInboundUserMessage === true && hasPromptImageInput(optionsLocal)) {
2643
+ assertSteeringActive();
2644
+ try {
2645
+ await cancelPendingUserInput("image-reply");
2646
+ } catch (error) {
2647
+ embeddedAgentLog.warn("failed to cancel codex gateway question before image steering", { error });
2648
+ }
2358
2649
  }
2359
2650
  try {
2360
2651
  await activeSteeringQueue.queue(text, optionsLocal);
@@ -2369,7 +2660,17 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2369
2660
  const handle = {
2370
2661
  kind: "embedded",
2371
2662
  runId: params.runId,
2663
+ startedAtMs: params.startedAtMs,
2372
2664
  toolAuthorityFingerprint: params.toolAuthorityFingerprint,
2665
+ permissionChangeOwner: params.permissionChange?.owner,
2666
+ applyPermissionMode: async (mode, revokeApprovals) => {
2667
+ if (!params.permissionChange || terminalState.terminalOutcomeFrozen || params.abortSignal?.aborted || !state.permissionChangeRestart && (state.completed || runAbortController.signal.aborted)) return false;
2668
+ const applied = params.permissionChange.request(mode);
2669
+ state.permissionChangeRestart ??= "requested";
2670
+ runAbortController.abort("permission-change");
2671
+ revokeApprovals();
2672
+ return await applied;
2673
+ },
2373
2674
  claimPendingUserInputAnswer,
2374
2675
  cancelPendingUserInput,
2375
2676
  queueMessage,
@@ -2383,7 +2684,7 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2383
2684
  isAbortable: () => !terminalState.terminalOutcomeFrozen || terminalState.sharedAbortAllowedAfterTerminalOutcome,
2384
2685
  isCompacting: () => projectorRef.current?.isCompacting() ?? false,
2385
2686
  supportsTranscriptCommitWait: true,
2386
- supportsQueueMessageImages: true,
2687
+ supportsQueueMessageImages: params.model.input.includes("image"),
2387
2688
  sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
2388
2689
  taskSuggestionDeliveryMode: params.taskSuggestionDeliveryMode,
2389
2690
  cancel: () => abortExplicitly("cancelled"),
@@ -2412,10 +2713,23 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2412
2713
  })().finally(completeTurn);
2413
2714
  return;
2414
2715
  }
2415
- interruptTurn(activeTurnId).finally(completeTurn);
2716
+ state.abortCleanup = interruptTurn(activeTurnId).then(async (confirmed) => {
2717
+ if (!terminalState.explicitCancellationObserved && !state.permissionChangeRestart) return;
2718
+ if (!confirmed) throw new Error(state.permissionChangeRestart ? "Permission change could not confirm the previous Codex turn stopped." : "Codex cancellation could not confirm the turn stopped; background terminals may still be running.");
2719
+ await terminateCodexBackgroundTerminals(resourceState.client, resourceState.thread.threadId);
2720
+ if (state.permissionChangeRestart) state.permissionChangeRestart = "confirmed";
2721
+ });
2722
+ state.abortCleanup.then(completeTurn, (error) => {
2723
+ embeddedAgentLog.warn("codex app-server cancellation cleanup failed", { error });
2724
+ completeTurn();
2725
+ });
2416
2726
  };
2417
2727
  runAbortController.signal.addEventListener("abort", abortListener, { once: true });
2418
2728
  if (runAbortController.signal.aborted) abortListener();
2729
+ else if (params.permissionChange && !params.permissionChange.applied()) {
2730
+ state.permissionChangeRestart = "requested";
2731
+ runAbortController.abort("permission-change");
2732
+ }
2419
2733
  return {
2420
2734
  activeTurnId,
2421
2735
  activeProjector,
@@ -2437,6 +2751,8 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2437
2751
  const { codexModelCallDiagnostics } = requestRuntime;
2438
2752
  const { activeTurnId, abortListener, handle, freezeRunTerminalOutcome } = activeTurn;
2439
2753
  prompt.context.attemptTools.scheduledAppAuthoritySourceRef.current = void 0;
2754
+ freezeRunTerminalOutcome();
2755
+ await state.abortCleanup.catch(() => void 0);
2440
2756
  try {
2441
2757
  steeringQueueRef.current?.cancel();
2442
2758
  if (params.isFinalFallbackAttempt !== false) await maybeEmitFastModeAutoResetBestEffort();
@@ -2457,18 +2773,14 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2457
2773
  aborted: runAbortController.signal.aborted && !state.clientClosedAbort
2458
2774
  });
2459
2775
  await runCleanupStep("codex-trajectory-flush", () => trajectoryRecorder?.flush());
2460
- const retainLiveIncognitoThread = terminalState.turnSucceeded && isIncognitoSessionKey(params.sessionKey);
2776
+ const retainLiveIncognitoThread = (terminalState.turnSucceeded || state.permissionChangeRestart === "confirmed" && !params.abortSignal?.aborted) && isIncognitoSessionKey(params.sessionKey);
2461
2777
  const retainedPersistentThread = terminalState.turnSucceeded && !isIncognitoSessionKey(params.sessionKey) && params.cleanupBundleMcpOnRunEnd !== true && resourceState.thread.liveThreadConfigFingerprint !== void 0 && resourceState.thread.clientId === resolveCodexAppServerClientInstanceId(resourceState.client) && resourceState.thread.preserveNativeModel !== true && resourceState.thread.connectionScope !== "supervision" && !resourceState.thread.ringZeroConfigFingerprint ? (await bindingStore.read(bindingIdentity))?.threadId === resourceState.thread.threadId && await bindingStore.withLease(bindingIdentity, async () => {
2462
2778
  if ((await bindingStore.read(bindingIdentity))?.threadId !== resourceState.thread.threadId) return false;
2463
- return await retainCodexAppServerLiveThread(resourceState.client, resourceState.thread.threadId, resourceState.thread.liveThreadOwnership?.release ?? (async (threadId) => {
2464
- if (!await unsubscribeCodexThreadBestEffort(resourceState.client, {
2465
- threadId,
2466
- timeoutMs: 5e3
2467
- })) {
2468
- await closeCodexStartupClientBestEffort(resourceState.client);
2469
- throw new CodexAppServerUnsafeSubscriptionError(`Codex retained thread subscription could not be released: ${threadId}`);
2470
- }
2471
- }), resourceState.thread.liveThreadConfigFingerprint, connection.mutable.pluginAppServer.serviceTier);
2779
+ return await retainCodexAppServerBindingSubscription(resourceState.client, resourceState.thread.threadId, {
2780
+ release: resourceState.thread.liveThreadOwnership?.release,
2781
+ configFingerprint: resourceState.thread.liveThreadConfigFingerprint,
2782
+ serviceTier: connection.mutable.pluginAppServer.serviceTier
2783
+ });
2472
2784
  }) : false;
2473
2785
  const retainLiveThread = retainLiveIncognitoThread || retainedPersistentThread;
2474
2786
  const bindingReleased = isIncognitoSessionKey(params.sessionKey) && !retainLiveIncognitoThread ? await bindingStore.mutate(bindingIdentity, {
@@ -2486,7 +2798,13 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2486
2798
  } finally {
2487
2799
  await runCleanupStep("codex-user-input-cancel", () => userInputBridgeRef.current?.cancelPending());
2488
2800
  await runCleanupStep("codex-turn-watch-clear", () => turnWatches.clearAllTimers());
2801
+ await runCleanupStep("codex-dynamic-tool-cleanup", async () => {
2802
+ const cleanupReason = terminalState.turnSucceeded ? "completion" : state.timedOut ? "timeout" : runAbortController.signal.aborted ? "cancel" : "error";
2803
+ const cleanups = prompt.context.attemptTools.runCleanups.splice(0);
2804
+ await Promise.allSettled(cleanups.map(async (cleanup) => await cleanup(cleanupReason)));
2805
+ });
2489
2806
  await runCleanupStep("codex-route-release", releaseCurrentRoute);
2807
+ await runCleanupStep("codex-transcript-checkpoint", () => activeTurn.activeProjector.transcriptCheckpoint.flush(true));
2490
2808
  await runCleanupStep("codex-shared-client-release", releaseSharedClientLeaseAndRetireOneShotClient);
2491
2809
  const nativeHookRelay = resourceState.nativeHookRelay;
2492
2810
  resourceState.nativeHookRelay = void 0;
@@ -2505,12 +2823,12 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2505
2823
  runAbortController.signal.removeEventListener("abort", abortListener);
2506
2824
  });
2507
2825
  await runCleanupStep("codex-steering-cancel", () => steeringQueueRef.current?.cancel());
2508
- await runCleanupStep("codex-terminal-freeze", freezeRunTerminalOutcome);
2509
2826
  await runCleanupStep("codex-reply-backend-detach", () => params.replyOperation?.detachBackend(handle));
2510
2827
  await runCleanupStep("codex-active-run-clear", () => {
2511
2828
  clearActiveEmbeddedRun(params.sessionId, handle, params.sessionKey, params.sessionFile);
2512
2829
  });
2513
2830
  }
2831
+ await state.abortCleanup;
2514
2832
  }
2515
2833
  //#endregion
2516
2834
  //#region extensions/codex/src/app-server/workspace-dir-cache.ts
@@ -2579,125 +2897,26 @@ async function runCodexAgentEndHook(params, hookParams) {
2579
2897
  runAgentEndSideEffects(sideEffectParams);
2580
2898
  }
2581
2899
  //#endregion
2582
- //#region extensions/codex/src/app-server/session-permission-policy.ts
2583
- function tupleForMode(mode, canUseAutoReview) {
2584
- switch (mode) {
2585
- case "read-only": return {
2586
- sandbox: "read-only",
2587
- approvalPolicy: "on-request",
2588
- approvalsReviewer: "user"
2589
- };
2590
- case "guarded": return {
2591
- sandbox: "workspace-write",
2592
- approvalPolicy: "on-request",
2593
- approvalsReviewer: "user"
2594
- };
2595
- case "workspace": return {
2596
- sandbox: "workspace-write",
2597
- approvalPolicy: "on-request",
2598
- approvalsReviewer: canUseAutoReview ? "auto_review" : "user"
2599
- };
2600
- case "full": return {
2601
- sandbox: "danger-full-access",
2602
- approvalPolicy: "never",
2603
- approvalsReviewer: "user"
2604
- };
2605
- }
2606
- return mode;
2607
- }
2608
- function requirementsAllowTuple(tuple, allowed) {
2609
- return (allowed.sandboxes === void 0 || allowed.sandboxes.has(tuple.sandbox)) && (allowed.approvalPolicies === void 0 || allowed.approvalPolicies.has(tuple.approvalPolicy)) && (allowed.reviewers === void 0 || allowed.reviewers.has(tuple.approvalsReviewer));
2610
- }
2611
- function tightenTupleForExecMode(tuple, execMode) {
2612
- switch (execMode) {
2613
- case "deny":
2614
- case "allowlist": return {
2615
- sandbox: "read-only",
2616
- approvalPolicy: "on-request",
2617
- approvalsReviewer: "user"
2618
- };
2619
- case "ask": return {
2620
- ...tuple,
2621
- approvalPolicy: "on-request",
2622
- approvalsReviewer: "user"
2623
- };
2624
- case "auto":
2625
- case "full":
2626
- case void 0: return tuple;
2627
- }
2628
- return execMode;
2629
- }
2630
- function clampSessionPermissionTuple(params) {
2631
- if (!params.requirementsToml) return params.requested;
2632
- const allowed = {
2633
- sandboxes: parseAllowedSandboxModesFromCodexRequirements(params.requirementsToml, params.hostName ?? hostname()),
2634
- approvalPolicies: parseAllowedApprovalPoliciesFromCodexRequirements(params.requirementsToml),
2635
- reviewers: parseAllowedApprovalsReviewersFromCodexRequirements(params.requirementsToml)
2636
- };
2637
- if (requirementsAllowTuple(params.requested, allowed)) return params.requested;
2638
- const userReviewRequired = params.mode === "read-only" || params.mode === "guarded" || params.mode === "workspace" && !params.canUseAutoReview;
2639
- return {
2640
- sandbox: selectGuardianSandbox(allowed.sandboxes),
2641
- approvalPolicy: selectGuardianApprovalPolicy(allowed.approvalPolicies, userReviewRequired ? "ask" : "auto"),
2642
- approvalsReviewer: userReviewRequired ? selectUserApprovalsReviewer(allowed.reviewers) : selectGuardianApprovalsReviewer(allowed.reviewers, "auto")
2643
- };
2644
- }
2645
- /** Applies one complete session-mode tuple without mixing requirements-clamped fields. */
2646
- function applyCodexSessionPermissionPolicy(params) {
2647
- if (!params.permissionMode) return params.appServer;
2648
- const sessionRoot = params.sessionRoot?.trim();
2649
- if (!sessionRoot) throw new Error("Codex session permission mode requires a recorded session root");
2650
- if (params.policyLocked) return {
2651
- ...params.appServer,
2652
- sessionRoot
2653
- };
2654
- const requested = tightenTupleForExecMode(tupleForMode(params.permissionMode, params.canUseAutoReview), params.execMode);
2655
- const tuple = params.appServer.start.transport === "stdio" ? clampSessionPermissionTuple({
2656
- mode: params.permissionMode,
2657
- requested,
2658
- requirementsToml: params.requirementsToml,
2659
- hostName: params.hostName,
2660
- canUseAutoReview: params.canUseAutoReview
2661
- }) : requested;
2662
- const networkProxy = params.appServer.networkProxy ? resolveCodexAppServerNetworkProxy(params.pluginConfig.appServer?.networkProxy, tuple.sandbox).networkProxy : void 0;
2663
- const resolved = {
2664
- ...params.appServer,
2665
- ...tuple,
2666
- sessionRoot
2667
- };
2668
- if (networkProxy) resolved.networkProxy = networkProxy;
2669
- else delete resolved.networkProxy;
2670
- return resolved;
2671
- }
2672
- /** Keeps relative execution inside the prepared root without filesystem rediscovery. */
2673
- function resolveCodexSessionPermissionCwd(params) {
2674
- if (!params.permissionMode) return params.requestedCwd ?? params.fallbackCwd;
2675
- const sessionRoot = params.sessionRoot?.trim();
2676
- if (!sessionRoot) throw new Error("Codex session permission mode requires a recorded session root");
2677
- const requestedCwd = params.requestedCwd?.trim();
2678
- return requestedCwd && isPathInside(sessionRoot, requestedCwd) ? requestedCwd : sessionRoot;
2679
- }
2680
- //#endregion
2681
2900
  //#region extensions/codex/src/app-server/startup-binding.ts
2682
2901
  const CODEX_APP_SERVER_NATIVE_THREAD_FALLBACK_MAX_TOKENS = 3e5;
2683
2902
  const CODEX_APP_SERVER_NATIVE_THREAD_DEFAULT_RESERVE_TOKENS = 2e4;
2684
2903
  const CODEX_APP_SERVER_NATIVE_THREAD_MIN_PROMPT_BUDGET_TOKENS = 8e3;
2685
2904
  const CODEX_APP_SERVER_NATIVE_THREAD_MIN_PROMPT_BUDGET_RATIO = .5;
2686
- const CODEX_APP_SERVER_ROLLOUT_TAIL_READ_BYTES = 64 * 1024;
2905
+ const CODEX_APP_SERVER_ROLLOUT_TAIL_READ_BYTES = 65536;
2687
2906
  const CODEX_APP_SERVER_BYTE_UNITS = {
2688
2907
  b: 1,
2689
2908
  k: 1024,
2690
2909
  kb: 1024,
2691
2910
  kib: 1024,
2692
- m: 1024 * 1024,
2693
- mb: 1024 * 1024,
2694
- mib: 1024 * 1024,
2695
- g: 1024 * 1024 * 1024,
2696
- gb: 1024 * 1024 * 1024,
2697
- gib: 1024 * 1024 * 1024,
2698
- t: 1024 * 1024 * 1024 * 1024,
2699
- tb: 1024 * 1024 * 1024 * 1024,
2700
- tib: 1024 * 1024 * 1024 * 1024
2911
+ m: 1048576,
2912
+ mb: 1048576,
2913
+ mib: 1048576,
2914
+ g: 1073741824,
2915
+ gb: 1073741824,
2916
+ gib: 1073741824,
2917
+ t: 1099511627776,
2918
+ tb: 1099511627776,
2919
+ tib: 1099511627776
2701
2920
  };
2702
2921
  const codexSessionRecordCache = /* @__PURE__ */ new Map();
2703
2922
  function parseCodexAppServerByteLimit(value) {
@@ -2994,11 +3213,12 @@ async function prepareCodexAttemptConnection({ params, options }) {
2994
3213
  const pluginConfig = readCodexPluginConfig(options.pluginConfig);
2995
3214
  const requirementsToml = readCodexRequirementsToml({});
2996
3215
  const computerUseConfig = resolveCodexComputerUseConfig({ pluginConfig });
2997
- const { sessionAgentId } = resolveSessionAgentIds({
3216
+ const { sessionAgentId } = resolveSessionAgentIdsStrict({
2998
3217
  sessionKey: params.sessionKey,
2999
3218
  config: params.config,
3000
3219
  agentId: params.agentId
3001
3220
  });
3221
+ const policyAgentId = params.sandboxAgentId ?? sessionAgentId;
3002
3222
  preDynamicStartupStages.mark("config");
3003
3223
  const resolvedWorkspace = resolveUserPath(params.workspaceDir);
3004
3224
  await ensureCodexWorkspaceDirOnce(resolvedWorkspace);
@@ -3007,6 +3227,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3007
3227
  const contextSessionKey = params.sessionKey?.trim() || sandboxSessionKey;
3008
3228
  const sandbox = params.sandbox !== void 0 ? params.sandbox : await resolveSandboxContext({
3009
3229
  config: params.config,
3230
+ agentId: params.sandboxAgentId,
3010
3231
  sessionKey: sandboxSessionKey,
3011
3232
  workspaceDir: resolvedWorkspace
3012
3233
  });
@@ -3017,18 +3238,24 @@ async function prepareCodexAttemptConnection({ params, options }) {
3017
3238
  execOverrides: params.execOverrides,
3018
3239
  approvals: params.permissionMode === "full" ? void 0 : loadExecApprovals(),
3019
3240
  config: params.config,
3020
- agentId: sessionAgentId
3241
+ agentId: policyAgentId
3021
3242
  });
3022
3243
  const agentDir = params.agentDir ?? resolveAgentDir$1(params.config ?? {}, sessionAgentId);
3023
3244
  const preparedEnvironment = params.hostCapabilities.preparedEnvironment?.();
3024
3245
  const remoteExec = isCodexRemoteExecPlacementSandbox(sandbox);
3246
+ const assertLocalTargetSupported = (unsupported) => {
3247
+ 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.");
3248
+ };
3249
+ assertLocalTargetSupported(sandbox?.enabled === true || remoteExec);
3025
3250
  const preparedShellEnvironment = preparedEnvironment ? {
3026
3251
  ...preparedEnvironment.credentialScrubEnv,
3027
- ...!remoteExec ? preparedEnvironment.localIdentityEnv : void 0
3252
+ ...!remoteExec ? preparedEnvironment.localIdentityEnv : void 0,
3253
+ ...preparedEnvironment.localProcessEnv
3028
3254
  } : void 0;
3029
3255
  const shellEnvironment = preparedShellEnvironment && Object.keys(preparedShellEnvironment).length > 0 ? preparedShellEnvironment : void 0;
3030
- const disableLoginShell = remoteExec || preparedEnvironment?.managedLocalIdentity === true || preparedEnvironment !== void 0 && Object.keys(preparedEnvironment.credentialScrubEnv).length > 0;
3256
+ const disableLoginShell = remoteExec || preparedEnvironment?.localProcessEnv !== void 0 || preparedEnvironment?.managedLocalIdentity === true || preparedEnvironment !== void 0 && Object.keys(preparedEnvironment.credentialScrubEnv).length > 0;
3031
3257
  const withPreparedProcessEnv = (appServer) => {
3258
+ assertLocalTargetSupported(appServer.start.transport === "websocket" || Boolean(appServer.remoteWorkspaceRoot));
3032
3259
  return shellEnvironment ? {
3033
3260
  ...appServer,
3034
3261
  start: {
@@ -3150,6 +3377,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3150
3377
  const sessionPermissionCwd = resolveCodexSessionPermissionCwd({
3151
3378
  permissionMode: params.permissionMode,
3152
3379
  sessionRoot: params.sessionRoot,
3380
+ defaultRoot: effectiveWorkspace,
3153
3381
  requestedCwd,
3154
3382
  fallbackCwd: effectiveWorkspace
3155
3383
  });
@@ -3160,14 +3388,20 @@ async function prepareCodexAttemptConnection({ params, options }) {
3160
3388
  appServer,
3161
3389
  permissionMode: params.permissionMode,
3162
3390
  sessionRoot: params.sessionRoot,
3391
+ defaultRoot: effectiveWorkspace,
3163
3392
  pluginConfig,
3164
3393
  canUseAutoReview: canUseCodexModelBackedApprovalsReviewerForModel({
3165
3394
  modelProvider: selection.modelProvider,
3166
3395
  model: selection.model,
3167
3396
  config: params.config,
3168
- env: process.env,
3397
+ env: {
3398
+ ...process.env,
3399
+ ...appServer.start.env,
3400
+ ...shellEnvironment
3401
+ },
3169
3402
  agentDir,
3170
- homeScope: appServer.start.homeScope
3403
+ homeScope: appServer.start.homeScope,
3404
+ codexArgs: appServer.start.args
3171
3405
  }),
3172
3406
  requirementsToml,
3173
3407
  policyLocked: startupBinding?.connectionScope === "supervision",
@@ -3180,7 +3414,11 @@ async function prepareCodexAttemptConnection({ params, options }) {
3180
3414
  provider: selection.modelProvider,
3181
3415
  model: selection.model,
3182
3416
  config: params.config,
3183
- env: process.env,
3417
+ env: {
3418
+ ...process.env,
3419
+ ...session.start.env,
3420
+ ...shellEnvironment
3421
+ },
3184
3422
  agentDir
3185
3423
  });
3186
3424
  return {
@@ -3244,6 +3482,17 @@ async function prepareCodexAttemptConnection({ params, options }) {
3244
3482
  resolvedAppServer = resolveFinalAppServer(configuredAppServer, reviewerPolicyContext);
3245
3483
  appServer = resolvedAppServer.appServer;
3246
3484
  }
3485
+ const sessionPermissionPolicy = resolveCodexEffectiveSessionPermissionPolicy({
3486
+ appServer,
3487
+ permissionMode: params.permissionMode,
3488
+ sessionRoot: params.sessionRoot,
3489
+ defaultRoot: effectiveWorkspace
3490
+ });
3491
+ if (sessionPermissionPolicy) {
3492
+ params.permissionMode = sessionPermissionPolicy.mode;
3493
+ params.sessionRoot = sessionPermissionPolicy.root;
3494
+ (params.execOverrides ??= {}).mode = sessionPermissionPolicy.execMode;
3495
+ }
3247
3496
  const nativeHookRelayEvents = resolveCodexNativeHookRelayEvents({
3248
3497
  configuredEvents: options.nativeHookRelay?.events,
3249
3498
  appServer
@@ -3271,6 +3520,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3271
3520
  pluginConfig,
3272
3521
  computerUseConfig,
3273
3522
  sessionAgentId,
3523
+ policyAgentId,
3274
3524
  resolvedWorkspace,
3275
3525
  sandboxSessionKey,
3276
3526
  contextSessionKey,
@@ -3290,6 +3540,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3290
3540
  effectiveWorkspace,
3291
3541
  effectiveCwd,
3292
3542
  appServer,
3543
+ sessionPermissionPolicy,
3293
3544
  nativeHookRelayEvents,
3294
3545
  runAbortController,
3295
3546
  terminalState,
@@ -3327,7 +3578,7 @@ const CODEX_BOOTSTRAP_CONTEXT_ORDER = /* @__PURE__ */ new Map([
3327
3578
  /** Reads mirrored Codex session history for harness hooks. */
3328
3579
  async function readMirroredSessionHistoryMessages(params) {
3329
3580
  const { admission, ...target } = params;
3330
- const messages = await readCodexMirroredSessionHistoryMessages(target, admission);
3581
+ const messages = await readCodexMirroredSessionHistoryMessages(target, admission, "model-context");
3331
3582
  if (!messages) embeddedAgentLog.warn("failed to read mirrored session history for codex harness hooks", { sessionFile: params.sessionFile });
3332
3583
  return messages;
3333
3584
  }
@@ -3813,8 +4064,9 @@ async function renderCodexWorkspaceMemoryCollaborationInstructions(params) {
3813
4064
  return sections.length > 0 ? sections.join("\n\n") : void 0;
3814
4065
  }
3815
4066
  async function renderCodexMemoryRecallInstructions(params) {
4067
+ const availableTools = new Set(params.toolNames);
3816
4068
  const memoryPrompt = await prepareMemorySystemPromptAddition({
3817
- availableTools: new Set(params.toolNames),
4069
+ availableTools,
3818
4070
  citationsMode: params.citationsMode,
3819
4071
  agentId: params.agentId,
3820
4072
  agentSessionKey: params.agentSessionKey,
@@ -4011,7 +4263,7 @@ function buildCodexAppServerTimeoutDiagnostics(params) {
4011
4263
  //#region extensions/codex/src/app-server/run-attempt-context.ts
4012
4264
  async function prepareCodexAttemptContext(runtime, attemptTools) {
4013
4265
  const { connection, runtimeParams, activeSessionId, activeSessionFile, buildActiveRunAttemptParams, effectiveContextWindowInfo, effectiveContextTokenBudget, effectiveRuntimeProviderId, effectiveRuntimeModelId, hookChannelId } = runtime;
4014
- const { params, sessionAgentId, contextSessionKey, activeContextEngine, initialStartupBindingHadInactiveThreadBootstrap, sandboxSessionKey, effectiveWorkspace, effectiveCwd, agentDir, usesSupervisionConnection, resolvedWorkspace, initialInactiveThreadBootstrapBindingForcedFreshStart, sandbox } = connection;
4266
+ const { params, sessionAgentId, contextSessionKey, activeContextEngine, initialStartupBindingHadInactiveThreadBootstrap, effectiveWorkspace, effectiveCwd, agentDir, usesSupervisionConnection, resolvedWorkspace, initialInactiveThreadBootstrapBindingForcedFreshStart, sandbox } = connection;
4015
4267
  const { toolBridge } = attemptTools;
4016
4268
  const activeTranscriptTarget = {
4017
4269
  agentId: sessionAgentId,
@@ -4037,7 +4289,7 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
4037
4289
  const hookContext = {
4038
4290
  runId: params.runId,
4039
4291
  agentId: sessionAgentId,
4040
- sessionKey: sandboxSessionKey,
4292
+ sessionKey: contextSessionKey,
4041
4293
  sessionId: params.sessionId,
4042
4294
  workspaceDir: params.workspaceDir,
4043
4295
  messageProvider: params.messageProvider ?? void 0,
@@ -4345,101 +4597,17 @@ function isInvalidCodexImagePayloadError(message) {
4345
4597
  return /\b(?:invalid|malformed)\b[\s\S]{0,120}\b(?:image|image url|base64)\b/iu.test(normalizedMessage) || /\b(?:image|image url|base64)\b[\s\S]{0,120}\b(?:invalid|malformed)\b/iu.test(normalizedMessage);
4346
4598
  }
4347
4599
  //#endregion
4348
- //#region extensions/codex/src/app-server/settled-turn-context.ts
4349
- function collectUniqueMessageIdentities(messages) {
4350
- const identities = /* @__PURE__ */ new Map();
4351
- for (const [index, message] of messages.entries()) {
4352
- const identity = readMirrorIdentity(message);
4353
- if (!identity) continue;
4354
- if (identities.has(identity)) return;
4355
- identities.set(identity, index);
4356
- }
4357
- return identities;
4358
- }
4359
- function adoptPersistedHostPrompt(params) {
4360
- const promptIdentity = `${params.turnId}:prompt`;
4361
- if (params.mirroredMessages.some((message) => readMirrorIdentity(message) === promptIdentity)) return params;
4362
- const sourcePrompt = params.settledMessages[0];
4363
- const sourceKey = sourcePrompt?.idempotencyKey;
4364
- if (sourcePrompt?.role !== "user" || readMirrorIdentity(sourcePrompt) !== promptIdentity || typeof sourceKey !== "string" || sourceKey.trim().length === 0) return params;
4365
- const matches = params.historyMessages.flatMap((message, index) => message.role === "user" && message.idempotencyKey === sourceKey ? [{
4366
- index,
4367
- message
4368
- }] : []);
4369
- const persistedPrompt = matches.length === 1 ? matches[0] : void 0;
4370
- const persistedMetadata = persistedPrompt?.message;
4371
- if (!persistedPrompt || readMirrorIdentity(persistedPrompt.message) !== void 0 || readCodexMirrorSourceFingerprint(persistedPrompt.message) !== void 0 || persistedMetadata?.["__openclaw"]?.mirrorOrigin === "codex-app-server") return params;
4372
- const sourceUpstreamText = readUpstreamUserText(sourcePrompt);
4373
- const persistedUpstreamText = readUpstreamUserText(persistedPrompt.message);
4374
- if (persistedUpstreamText !== void 0 && persistedUpstreamText !== sourceUpstreamText) return params;
4375
- let logicalPrompt = attachCodexMirrorIdentity(persistedPrompt.message, promptIdentity);
4376
- if (sourceUpstreamText !== void 0) logicalPrompt = attachUpstreamUserText(logicalPrompt, sourceUpstreamText);
4377
- if (serializeCodexMirrorSourceEvidence(logicalPrompt) !== serializeCodexMirrorSourceEvidence(sourcePrompt)) return params;
4378
- const historyMessages = [...params.historyMessages];
4379
- historyMessages[persistedPrompt.index] = logicalPrompt;
4380
- return {
4381
- historyMessages,
4382
- mirroredMessages: [logicalPrompt, ...params.mirroredMessages]
4383
- };
4384
- }
4385
- /** Freezes one complete active transcript branch through the settled tool-result boundary. */
4386
- function buildCodexSettledTurnFinalizationContext(params) {
4387
- const { historyMessages, mirroredMessages } = adoptPersistedHostPrompt(params);
4388
- const boundaryMessage = params.settledMessages.findLast((message) => message.role === "toolResult");
4389
- const boundaryIdentity = boundaryMessage ? readMirrorIdentity(boundaryMessage) : void 0;
4390
- if (!boundaryMessage || !boundaryIdentity || !boundaryIdentity.startsWith(`${params.turnId}:tool:`)) return;
4391
- const settledBoundaryIndex = params.settledMessages.indexOf(boundaryMessage);
4392
- const requiredIdentities = params.settledMessages.slice(0, settledBoundaryIndex + 1).map(readMirrorIdentity);
4393
- if (requiredIdentities.length === 0 || requiredIdentities.some((identity) => !identity) || new Set(requiredIdentities).size !== requiredIdentities.length || !requiredIdentities.includes(`${params.turnId}:prompt`)) return;
4394
- const historyIdentities = collectUniqueMessageIdentities(historyMessages);
4395
- const mirroredIdentities = collectUniqueMessageIdentities(mirroredMessages);
4396
- if (!historyIdentities || !mirroredIdentities) return;
4397
- const mirroredBoundaryIndex = mirroredIdentities.get(boundaryIdentity);
4398
- if (mirroredBoundaryIndex === void 0) return;
4399
- const mirroredThroughBoundary = mirroredMessages.slice(0, mirroredBoundaryIndex + 1);
4400
- if (mirroredThroughBoundary.length !== requiredIdentities.length || mirroredThroughBoundary.some((message, index) => readMirrorIdentity(message) !== requiredIdentities[index])) return;
4401
- const historyBoundaryIndex = historyIdentities.get(boundaryIdentity);
4402
- if (historyBoundaryIndex === void 0) return;
4403
- let previousHistoryIndex = -1;
4404
- for (const mirroredMessage of mirroredThroughBoundary) {
4405
- const identity = readMirrorIdentity(mirroredMessage);
4406
- const historyIndex = identity ? historyIdentities.get(identity) : void 0;
4407
- const historyMessage = historyIndex === void 0 ? void 0 : historyMessages[historyIndex];
4408
- if (historyIndex === void 0 || historyIndex <= previousHistoryIndex || historyIndex > historyBoundaryIndex || !historyMessage || serializeCodexMirrorSourceEvidence(historyMessage) !== serializeCodexMirrorSourceEvidence(mirroredMessage)) return;
4409
- previousHistoryIndex = historyIndex;
4410
- }
4411
- return {
4412
- source: "openclaw-transcript",
4413
- messages: Object.freeze(structuredClone(params.historyMessages.slice(0, historyBoundaryIndex + 1)))
4414
- };
4415
- }
4416
- /** Reads and freezes the current active transcript branch after mirroring has settled. */
4417
- async function captureCodexSettledTurnFinalizationContext(params) {
4418
- try {
4419
- const historyMessages = await readCodexMirroredSessionHistoryMessages(params);
4420
- if (!historyMessages) return;
4421
- return buildCodexSettledTurnFinalizationContext({
4422
- historyMessages,
4423
- mirroredMessages: params.mirroredMessages,
4424
- settledMessages: params.settledMessages,
4425
- turnId: params.turnId
4426
- });
4427
- } catch (error) {
4428
- embeddedAgentLog.warn("codex settled-turn finalization context capture failed", {
4429
- error: formatErrorMessage(error),
4430
- turnId: params.turnId
4431
- });
4432
- return;
4433
- }
4434
- }
4435
- //#endregion
4436
4600
  //#region extensions/codex/src/app-server/trajectory.ts
4437
4601
  /** Creates a trajectory recorder when the host exposes its capture capability. */
4438
4602
  function createCodexTrajectoryRecorder(params) {
4439
4603
  if (!params.trajectory) return null;
4440
4604
  const trajectory = params.trajectory;
4441
4605
  return {
4442
- recordEvent: trajectory.recordEvent,
4606
+ recordEvent: (type, data) => {
4607
+ try {
4608
+ trajectory.recordEvent(type, data);
4609
+ } catch {}
4610
+ },
4443
4611
  flush: trajectory.flush
4444
4612
  };
4445
4613
  }
@@ -4501,7 +4669,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4501
4669
  const { hookContextWindowFields, hookRunner } = context;
4502
4670
  const { connection, preparedAuthBinding } = runtime;
4503
4671
  const { effectiveRuntimeProviderId, effectiveRuntimeModelId } = runtime;
4504
- const { params, terminalState, runAbortController, activeContextEngine, bindingStore, bindingIdentity, appServer, usesSupervisionConnection, sessionAgentId, contextSessionKey, effectiveCwd, attemptStartedAt, startupAuthProfileId } = connection;
4672
+ const { params, terminalState, runAbortController, activeContextEngine, bindingStore, bindingIdentity, appServer, usesSupervisionConnection, sessionAgentId, contextSessionKey, effectiveCwd, agentDir, attemptStartedAt, startupAuthProfileId } = connection;
4505
4673
  const { toolBridge, toolState } = attemptTools;
4506
4674
  const { state, completion, pendingOpenClawDynamicToolCompletionIds, activeTurnItemIds, activeCompletionBlockerItemIds, activeFinalizationHookRunIds, turnWatches } = turnRuntime;
4507
4675
  const { emitLifecycleTerminal, buildLifecycleTerminalMeta } = lifecycle;
@@ -4509,7 +4677,20 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4509
4677
  const { codexModelCallDiagnostics } = requestRuntime;
4510
4678
  const { activeTurnId, activeProjector, streamState, freezeRunTerminalOutcome, notifyUserMessagePersisted } = activeTurn;
4511
4679
  await completion;
4512
- await drainNotificationQueue();
4680
+ await state.abortCleanup;
4681
+ const drain = drainNotificationQueue();
4682
+ const abortGraceElapsed = createDeferred();
4683
+ let abortGraceTimer;
4684
+ const abortListener = addAbortListener(runAbortController.signal, () => {
4685
+ abortGraceTimer = setTimeout(() => abortGraceElapsed.resolve(), TURN_FINALIZE_DRAIN_ABORT_GRACE_MS);
4686
+ abortGraceTimer.unref?.();
4687
+ });
4688
+ try {
4689
+ await Promise.race([drain, abortGraceElapsed.promise]);
4690
+ } finally {
4691
+ abortListener[Symbol.dispose]();
4692
+ clearTimeout(abortGraceTimer);
4693
+ }
4513
4694
  const hasQuiescentCompletedAssistant = activeProjector.hasCompletedTerminalAssistantText() && state.activeAppServerTurnRequests === 0 && activeTurnItemIds.size === 0 && activeCompletionBlockerItemIds.size === 0 && pendingOpenClawDynamicToolCompletionIds.size === 0 && activeFinalizationHookRunIds.size === 0 && state.unsettledFinalizationHookCount === 0 && state.rejectedFinalizationHookAssistant === void 0;
4514
4695
  const hasRecoverableCompletedAssistant = !turnWatches.isCompletionIdleWatchPinnedByTerminalError() && turnWatches.isAssistantCompletionIdleWatchArmed() && hasQuiescentCompletedAssistant;
4515
4696
  const recoveredTurnWatchTimeout = state.turnCompletionIdleTimedOut && !terminalState.explicitCancellationObserved && hasRecoverableCompletedAssistant && activeProjector.recoverCompletedTerminalAssistantAfterTurnWatchTimeout();
@@ -4638,14 +4819,13 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4638
4819
  turnId: activeTurnId
4639
4820
  });
4640
4821
  const { assistantTranscriptOwned, assistantTranscriptIdempotencyKey, terminalAnchor } = mirrorOutcome;
4641
- const shouldCaptureSettledTurnFinalizationContext = result.assistantTexts.every((text) => !text.trim()) && result.messagesSnapshot.some((message) => message.role === "toolResult") && (!finalPromptError || activeProjector.settledTurnFailureFinalizationAllowed);
4642
- const settledTurnFinalizationContext = shouldCaptureSettledTurnFinalizationContext ? await captureCodexSettledTurnFinalizationContext({
4822
+ const settledTurnFinalizationContext = result.assistantTexts.every((text) => !text.trim()) && result.messagesSnapshot.some((message) => message.role === "toolResult") && (!finalPromptError || activeProjector.settledTurnFailureFinalizationAllowed) ? await captureCodexSettledTurnFinalizationContext({
4643
4823
  ...activeTranscriptTarget,
4644
4824
  mirroredMessages: mirrorOutcome.mirroredMessages,
4645
4825
  settledMessages: result.messagesSnapshot,
4646
4826
  turnId: activeTurnId
4647
- }) : void 0;
4648
- if (shouldCaptureSettledTurnFinalizationContext && !settledTurnFinalizationContext) embeddedAgentLog.warn("codex settled-turn finalization context is unavailable", {
4827
+ }) ?? Object.freeze({ source: "unavailable" }) : void 0;
4828
+ if (settledTurnFinalizationContext?.source === "unavailable") embeddedAgentLog.warn("codex settled-turn finalization context is unavailable", {
4649
4829
  threadId: resourceState.thread.threadId,
4650
4830
  turnId: activeTurnId
4651
4831
  });
@@ -4680,20 +4860,11 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4680
4860
  modelIterations: result.modelIterations ?? 0,
4681
4861
  skillWorkshopAvailable: flattenCodexDynamicToolFunctions(attemptTools.toolBridge.availableSpecs).some((tool) => tool.name === "skill_workshop"),
4682
4862
  compacted: (result.compactionCount ?? 0) > 0,
4683
- messageChannel: params.messageChannel,
4684
- messageProvider: params.messageProvider,
4685
- chatType: params.chatType,
4686
- agentAccountId: params.agentAccountId,
4687
- groupId: params.groupId,
4688
- groupChannel: params.groupChannel,
4689
- groupSpace: params.groupSpace,
4690
- memberRoleIds: params.memberRoleIds,
4691
- spawnedBy: params.spawnedBy,
4692
4863
  senderId: params.senderId ?? void 0,
4693
- senderName: params.senderName,
4694
- senderUsername: params.senderUsername,
4695
- senderE164: params.senderE164,
4696
- senderIsOwner: params.senderIsOwner
4864
+ foregroundPromptContext: buildEmbeddedForegroundPromptContext({
4865
+ ...params,
4866
+ agentId: sessionAgentId
4867
+ }, agentDir)
4697
4868
  },
4698
4869
  hookRunner
4699
4870
  });
@@ -4757,8 +4928,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4757
4928
  yielded: toolState.yieldDetected
4758
4929
  })
4759
4930
  });
4760
- const finalizedResult = {
4761
- ...result,
4931
+ const finalizedResult = Object.assign(result, {
4762
4932
  ...toolState.yieldAcknowledgment ? { yieldAcknowledgment: toolState.yieldAcknowledgment } : {},
4763
4933
  terminal: attemptTerminal.normalize({
4764
4934
  timedOut: effectiveTimedOut,
@@ -4776,7 +4946,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4776
4946
  ...resourceState.runtimeContinuationStarted ? { runtimeContinuationStarted: true } : {},
4777
4947
  ...!finalAborted && !effectiveTimedOut && !finalPromptError && preparedAuthBinding ? { authBindingFingerprint: preparedAuthBinding.fingerprint } : {},
4778
4948
  systemPromptReport
4779
- };
4949
+ });
4780
4950
  if (turnSucceeded && toolState.yieldDetected && !runAbortController.signal.aborted) resourceState.nativeHookRelay?.authorizeRetentionAfterSuccessfulYield();
4781
4951
  return finalizedResult;
4782
4952
  }
@@ -4860,9 +5030,10 @@ function applyCodexTurnNotificationState(params) {
4860
5030
  else if (shouldArmNoToolPostProgressReplyWatch || shouldArmNoToolPostRawProgressReplyWatch) armPostProgressReplyWatch();
4861
5031
  else if (trackedDynamicToolCompletion) armPostToolContinuationWatch();
4862
5032
  else if (unblockedAssistantCompletionRelease) turnWatches.armAssistantCompletionIdleWatch(describeNotificationActivity(notification));
4863
- else if (shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem) if (shouldUsePostToolContinuationWatch) armPostToolContinuationWatch();
4864
- else turnWatches.armCompletionIdleWatch();
4865
- else if (rawResponseItemCompletedWithNoActiveItems) turnWatches.armCompletionIdleWatch();
5033
+ else if (shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem) {
5034
+ if (shouldUsePostToolContinuationWatch) armPostToolContinuationWatch();
5035
+ else turnWatches.armCompletionIdleWatch();
5036
+ } else if (rawResponseItemCompletedWithNoActiveItems) turnWatches.armCompletionIdleWatch();
4866
5037
  else if (isCurrentTurnNotification && rawToolOutputCompletion) armPostToolContinuationWatch();
4867
5038
  else if (isCurrentTurnNotification && shouldDisarmAssistantCompletionIdleWatch(notification)) turnWatches.disarmAssistantCompletionIdleWatch();
4868
5039
  if (turnWatches.isCompletionIdleWatchArmed() && !turnWatches.isCompletionIdleWatchPinnedByTerminalError() && notification.method !== "turn/completed" && isCurrentTurnNotification && !trackedDynamicToolCompletion && !rawToolOutputCompletion && !postToolProgressNeedsTerminalGuard && !postToolPatchUpdateNeedsTerminalGuard && !rawResponseItemCompletedWithNoActiveItems && !shouldArmNoToolPostProgressReplyWatch && !shouldArmNoToolPostRawProgressReplyWatch && !shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem) turnWatches.disarmCompletionIdleWatch();
@@ -4976,7 +5147,7 @@ function createCodexAttemptLifecycleController(resources, turnRuntime) {
4976
5147
  state.lifecycleStarted = true;
4977
5148
  };
4978
5149
  const emitLifecycleTerminal = (data) => {
4979
- if (!state.lifecycleStarted || state.lifecycleTerminalEmitted) return;
5150
+ if (!state.lifecycleStarted || state.lifecycleTerminalEmitted || state.permissionChangeRestart) return;
4980
5151
  emitCodexAppServerEvent(params, {
4981
5152
  stream: "lifecycle",
4982
5153
  data: {
@@ -5224,7 +5395,6 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
5224
5395
  }
5225
5396
  //#endregion
5226
5397
  //#region extensions/codex/src/app-server/run-attempt-prompt.ts
5227
- const CODEX_META_KEY = "__openclaw";
5228
5398
  function isRestrictivePromptToolsAllow(toolsAllow) {
5229
5399
  return toolsAllow !== void 0 && !toolsAllow.some((name) => name.trim() === "*");
5230
5400
  }
@@ -5323,7 +5493,7 @@ async function prepareCodexAttemptPrompt(context) {
5323
5493
  if (isRestrictivePromptToolsAllow(toolsAllow)) throw new Error("Codex app-server cannot enforce before_prompt_build toolsAllow; use the embedded or Copilot runtime for turn-scoped tool policy.");
5324
5494
  return promptState.developerInstructions;
5325
5495
  } },
5326
- messages: structuredClone(historyState.messages),
5496
+ messages: historyState.messages,
5327
5497
  ctx: hookContext,
5328
5498
  bootstrapContextRunKind: params.bootstrapContextRunKind,
5329
5499
  toolAuthority: {
@@ -5401,11 +5571,9 @@ async function prepareCodexAttemptPrompt(context) {
5401
5571
  const cutoff = Date.parse(binding.historyCoveredThrough ?? "");
5402
5572
  return historyState.messages.filter((message) => {
5403
5573
  if (message.role !== "user" && message.role !== "assistant") return false;
5404
- const meta = CODEX_META_KEY in message ? message[CODEX_META_KEY] : void 0;
5405
- const mirrorIdentity = meta && typeof meta === "object" && !Array.isArray(meta) ? meta.mirrorIdentity : void 0;
5406
- const mirrorOrigin = meta && typeof meta === "object" && !Array.isArray(meta) ? meta.mirrorOrigin : void 0;
5574
+ const mirrorIdentity = readMirrorIdentity(message);
5407
5575
  const timestamp = typeof message.timestamp === "number" ? message.timestamp : typeof message.timestamp === "string" ? Date.parse(message.timestamp) : NaN;
5408
- return !("idempotencyKey" in message && typeof message.idempotencyKey === "string" && message.idempotencyKey.startsWith("codex-app-server:")) && mirrorOrigin !== "codex-app-server" && !(typeof mirrorIdentity === "string" && mirrorIdentity.startsWith("codex-app-server:")) && Number.isFinite(timestamp) && timestamp > (Number.isFinite(cutoff) ? cutoff : 0);
5576
+ 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);
5409
5577
  });
5410
5578
  };
5411
5579
  const applyResumeStaleBindingContinuityProjection = (binding) => {
@@ -5437,7 +5605,8 @@ async function prepareCodexAttemptPrompt(context) {
5437
5605
  return projected;
5438
5606
  };
5439
5607
  const applyNoContextEngineContinuityProjection = (action, binding) => {
5440
- if (activeContextEngine || !historyState.messages.some((message) => message.role === "user")) return false;
5608
+ const hasContinuity = historyState.messages.some((message) => message.role === "user" || action === "started" && (message.role === "compactionSummary" || message.role === "branchSummary"));
5609
+ if (activeContextEngine || !hasContinuity) return false;
5441
5610
  if (action === "resumed" && promptState.precomputedStaleBindingContinuityProjectionApplied) return true;
5442
5611
  if (action === "started" && promptState.staleBindingContinuityForcedFreshStart) return true;
5443
5612
  if (action === "started" && promptState.inactiveThreadBootstrapBindingForcedFreshStart) return false;
@@ -5530,7 +5699,7 @@ function prepareCodexAttemptResources(prompt) {
5530
5699
  const { context, turnState, buildRenderedCodexDeveloperInstructions } = prompt;
5531
5700
  const { runtime, attemptTools } = context;
5532
5701
  const { connection, hookChannelId } = runtime;
5533
- const { appServer, params, effectiveCwd, sessionAgentId, sandboxSessionKey, runAbortController, sandbox, options, nativeHookRelayEvents } = connection;
5702
+ const { appServer, params, effectiveCwd, sessionAgentId, contextSessionKey, runAbortController, sandbox, options, nativeHookRelayEvents } = connection;
5534
5703
  const { toolBridge } = attemptTools;
5535
5704
  const trajectoryRecorder = createCodexTrajectoryRecorder({
5536
5705
  attempt: params,
@@ -5570,7 +5739,7 @@ function prepareCodexAttemptResources(prompt) {
5570
5739
  emitCodexNativePreToolUseFailureDiagnostic({
5571
5740
  agentId: sessionAgentId,
5572
5741
  sessionId: params.sessionId,
5573
- sessionKey: sandboxSessionKey,
5742
+ sessionKey: contextSessionKey,
5574
5743
  runId: params.runId,
5575
5744
  signal: runAbortController.signal,
5576
5745
  failure,
@@ -5692,7 +5861,7 @@ function prepareCodexAttemptResources(prompt) {
5692
5861
  events: nativeHookRelayEvents,
5693
5862
  agentId: sessionAgentId,
5694
5863
  sessionId: params.sessionId,
5695
- sessionKey: sandboxSessionKey,
5864
+ sessionKey: contextSessionKey,
5696
5865
  config: params.config,
5697
5866
  runId: params.runId,
5698
5867
  channelId: hookChannelId,
@@ -5854,10 +6023,6 @@ function resolveScheduledCodexAppCreatorCaptureDecision(params) {
5854
6023
  ...unavailableReason ? { unavailableReason } : {}
5855
6024
  };
5856
6025
  }
5857
- function readConnectorId(tool) {
5858
- const meta = asOptionalRecord(asOptionalRecord(tool)?.["_meta"]);
5859
- return normalizeOptionalString(meta?.connector_id) ?? normalizeOptionalString(meta?.connectorId);
5860
- }
5861
6026
  function normalizeApprovalMode(value) {
5862
6027
  return value === "allow" || value === "deny" || value === "auto" || value === "ask" ? value : void 0;
5863
6028
  }
@@ -5924,7 +6089,7 @@ async function readCodexScheduledAppToolNamesByApp(params) {
5924
6089
  if (!isJsonObject(status) || !isJsonObject(status.tools)) throw new Error("Codex scheduled app inventory contained an invalid server status");
5925
6090
  if (status.name !== CODEX_APPS_MCP_SERVER) continue;
5926
6091
  for (const [toolName, tool] of Object.entries(status.tools)) {
5927
- const connectorId = readConnectorId(tool);
6092
+ const connectorId = readCodexMcpToolConnectorId(tool);
5928
6093
  if (connectorId) {
5929
6094
  const names = toolNamesByApp.get(connectorId) ?? /* @__PURE__ */ new Set();
5930
6095
  names.add(toolName);
@@ -5953,7 +6118,8 @@ async function readCurrentCodexScheduledAppPolicy$1(params) {
5953
6118
  };
5954
6119
  }
5955
6120
  function readToolApprovalMode(config, appId, toolName, fallback = "auto") {
5956
- return normalizeAppToolApprovalMode(asOptionalRecord(asOptionalRecord(asOptionalRecord(asOptionalRecord(config.apps)?.[appId])?.tools)?.[toolName])?.approval_mode) ?? fallback;
6121
+ const app = asOptionalRecord(asOptionalRecord(config.apps)?.[appId]);
6122
+ return normalizeAppToolApprovalMode(asOptionalRecord(asOptionalRecord(app?.tools)?.[toolName])?.approval_mode) ?? fallback;
5957
6123
  }
5958
6124
  /** Captures only apps callable on the exact active Codex client/thread. */
5959
6125
  async function captureScheduledCodexAppAuthority(params) {
@@ -6071,7 +6237,8 @@ function intersectCodexPluginThreadConfigWithScheduledAuthority(config, authorit
6071
6237
  destructiveApprovalMode: stricterApprovalMode(defaultApprovalMode(current), captured.destructiveApprovalMode)
6072
6238
  };
6073
6239
  }
6074
- const policyContext = buildPluginAppPolicyContext(apps, Object.fromEntries(Object.entries(config.policyContext.pluginAppIds).map(([key, ids]) => [key, ids.filter((id) => Object.hasOwn(apps, id))]).filter(([, ids]) => ids.length > 0)));
6240
+ const pluginAppIds = Object.fromEntries(Object.entries(config.policyContext.pluginAppIds).map(([key, ids]) => [key, ids.filter((id) => Object.hasOwn(apps, id))]).filter(([, ids]) => ids.length > 0));
6241
+ const policyContext = buildPluginAppPolicyContext(apps, pluginAppIds);
6075
6242
  const configPatch = buildCodexPluginAppsConfigPatchFromPolicyContext(policyContext);
6076
6243
  const appsPatch = asOptionalRecord(configPatch.apps);
6077
6244
  for (const [appId, captured] of capturedById) {
@@ -6140,7 +6307,7 @@ function resolveCodexAttemptBundleManifestRegistry(preparedModelRuntime) {
6140
6307
  return metadataSnapshot?.pluginIds === void 0 ? metadataSnapshot?.manifestRegistry : void 0;
6141
6308
  }
6142
6309
  async function prepareCodexAttemptRuntime(connection) {
6143
- const { params, pluginConfig, usesSupervisionConnection, appServer, startupAuthProfileId, startupPreparedAuth, startupClientAuthProfileId, agentDir, preDynamicStartupStages, effectiveWorkspace, contextSessionKey, sandboxSessionKey, sessionAgentId, sandbox, attemptClientFactory, runAbortController, activeContextEngine, mutable } = connection;
6310
+ const { params, pluginConfig, usesSupervisionConnection, appServer, startupAuthProfileId, startupPreparedAuth, startupClientAuthProfileId, agentDir, preDynamicStartupStages, effectiveWorkspace, contextSessionKey, sandboxSessionKey, sessionAgentId, policyAgentId, sandbox, attemptClientFactory, runAbortController, activeContextEngine, mutable } = connection;
6144
6311
  const preparedAuthBinding = !usesSupervisionConnection && appServer.start.homeScope !== "user" && startupAuthProfileId ? await prepareCodexAppServerAuthBinding({
6145
6312
  authProfileId: startupAuthProfileId,
6146
6313
  authProfileStore: params.authProfileStore,
@@ -6244,7 +6411,7 @@ async function prepareCodexAttemptRuntime(connection) {
6244
6411
  preDynamicStartupStages.mark("bundle-mcp");
6245
6412
  const sandboxExecServerEnabled = isCodexSandboxExecServerEnabled(pluginConfig, sandbox);
6246
6413
  const nativeToolSurfaceEnabled = shouldEnableCodexAppServerNativeToolSurface(runtimeParams, sandbox, {
6247
- agentId: sessionAgentId,
6414
+ agentId: policyAgentId,
6248
6415
  runtimeSessionKey: sandboxSessionKey,
6249
6416
  sandboxExecServerEnabled
6250
6417
  });
@@ -6518,6 +6685,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6518
6685
  data: {
6519
6686
  phase: "start",
6520
6687
  name: call.tool,
6688
+ itemId: call.callId,
6521
6689
  toolCallId: call.callId,
6522
6690
  ...toolMeta ? { meta: toolMeta } : {},
6523
6691
  ...toolArgs ? { args: toolArgs } : {},
@@ -6540,7 +6708,8 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6540
6708
  })) terminalDiagnosticObserved = true;
6541
6709
  }, { include: DYNAMIC_TOOL_TERMINAL_DIAGNOSTIC_TYPES });
6542
6710
  try {
6543
- const { execution } = openClawDynamicToolExecutions.claim(call, () => {
6711
+ const { execution } = openClawDynamicToolExecutions.claim(call, async () => {
6712
+ await projector?.transcriptCheckpoint.flush();
6544
6713
  emitDynamicToolStartedDiagnostic({
6545
6714
  call,
6546
6715
  agentId: sessionAgentId,
@@ -6548,7 +6717,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6548
6717
  sessionId: params.sessionId,
6549
6718
  sessionKey: params.sessionKey
6550
6719
  });
6551
- return handleDynamicToolCallWithTimeout({
6720
+ const response = await handleDynamicToolCallWithTimeout({
6552
6721
  call,
6553
6722
  toolBridge,
6554
6723
  signal,
@@ -6570,6 +6739,9 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6570
6739
  });
6571
6740
  }
6572
6741
  });
6742
+ recordCodexDynamicToolResult(projector, call, response, toCodexDynamicToolProtocolResponse(response));
6743
+ await projector?.transcriptCheckpoint.flush();
6744
+ return response;
6573
6745
  });
6574
6746
  const response = await execution;
6575
6747
  const protocolResponse = toCodexDynamicToolProtocolResponse(response);
@@ -6593,7 +6765,6 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6593
6765
  success: protocolResponse.success,
6594
6766
  contentItems: protocolResponse.contentItems
6595
6767
  });
6596
- recordCodexDynamicToolResult(projector, call, response, protocolResponse);
6597
6768
  if (protocolResponse.success && call.tool === "progress_card") {
6598
6769
  const progressCardInput = response.executedArguments ?? call.arguments;
6599
6770
  await projector?.recordDynamicProgressCardUpdate(progressCardInput);
@@ -6606,6 +6777,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6606
6777
  data: {
6607
6778
  phase: "result",
6608
6779
  name: call.tool,
6780
+ itemId: call.callId,
6609
6781
  toolCallId: call.callId,
6610
6782
  ...toolMeta ? { meta: toolMeta } : {},
6611
6783
  ...commandBearing ? { commandBearing: true } : {},
@@ -6681,7 +6853,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6681
6853
  //#region extensions/codex/src/app-server/attempt-runtime-artifact.ts
6682
6854
  async function verifyStartupArtifact(params) {
6683
6855
  if (!params.request) return;
6684
- const { readCodexAppServerClientRuntimeArtifact, validateCodexAppServerRuntimeArtifact } = await import("./runtime-artifact-BvyT1uyd.js");
6856
+ const { readCodexAppServerClientRuntimeArtifact, validateCodexAppServerRuntimeArtifact } = await import("./runtime-artifact-D9pYp4zL.js");
6685
6857
  const runtimeArtifact = readCodexAppServerClientRuntimeArtifact(params.client);
6686
6858
  const expected = params.request.expected;
6687
6859
  const matchesExpected = !expected || runtimeArtifact?.id === expected.id && runtimeArtifact.fingerprint === expected.fingerprint;
@@ -6711,19 +6883,17 @@ function startCodexComputerUseHealthMonitor(params) {
6711
6883
  }
6712
6884
  const fingerprint = buildComputerUseHealthMonitorFingerprint(params.config);
6713
6885
  const intervalMs = params.config.healthCheckIntervalMinutes * 6e4;
6714
- if (existing?.fingerprint === fingerprint && existing.repairComputerUseMcpChildren === params.repairComputerUseMcpChildren) return {
6886
+ if (existing?.fingerprint === fingerprint) return {
6715
6887
  started: false,
6716
6888
  intervalMs,
6717
6889
  reason: "already_started"
6718
6890
  };
6719
6891
  if (existing) clearComputerUseHealthMonitor(params.client, existing);
6720
- const repairComputerUseMcpChildren = params.repairComputerUseMcpChildren ?? (() => killStaleComputerUseMcpChildren({ ancestorPid: params.client.getTransportPid() }));
6721
6892
  const monitor = {
6722
6893
  fingerprint,
6723
6894
  intervalMs,
6724
- repairComputerUseMcpChildren: params.repairComputerUseMcpChildren,
6725
6895
  timer: setInterval(() => {
6726
- runCodexComputerUseHealthProbe(params.client, params.config, monitor, { repairComputerUseMcpChildren });
6896
+ runCodexComputerUseHealthProbe(params.client, params.config, monitor);
6727
6897
  }, intervalMs),
6728
6898
  disposeCloseHandler: () => void 0,
6729
6899
  running: false
@@ -6748,13 +6918,12 @@ function buildComputerUseHealthMonitorFingerprint(config) {
6748
6918
  toolCallTimeoutMs: config.toolCallTimeoutMs
6749
6919
  });
6750
6920
  }
6751
- async function runCodexComputerUseHealthProbe(client, config, monitor, options) {
6921
+ async function runCodexComputerUseHealthProbe(client, config, monitor) {
6752
6922
  if (monitor.running) return;
6753
6923
  monitor.running = true;
6754
6924
  try {
6755
6925
  const { liveTest, repair } = await runCodexComputerUseLiveTest({
6756
6926
  config,
6757
- repairComputerUseMcpChildren: options.repairComputerUseMcpChildren,
6758
6927
  request: async (method, requestParams, requestOptions) => await client.request(method, requestParams, { timeoutMs: requestOptions?.timeoutMs ?? config.liveTestTimeoutMs })
6759
6928
  });
6760
6929
  if (!liveTest.ok) {
@@ -6767,10 +6936,7 @@ async function runCodexComputerUseHealthProbe(client, config, monitor, options)
6767
6936
  });
6768
6937
  return;
6769
6938
  }
6770
- if (repair?.killedPids.length) embeddedAgentLog.info("codex computer-use periodic health repaired stale children", {
6771
- mcpServerName: config.mcpServerName,
6772
- killedPids: repair.killedPids
6773
- });
6939
+ if (repair?.attempted && repair.warnings.length === 0) embeddedAgentLog.info("codex computer-use periodic health reloaded MCP servers", { mcpServerName: config.mcpServerName });
6774
6940
  } catch (error) {
6775
6941
  embeddedAgentLog.warn("codex computer-use periodic health probe crashed", {
6776
6942
  mcpServerName: config.mcpServerName,
@@ -6865,6 +7031,7 @@ function createCodexPluginThreadConfigStartupProvider(params) {
6865
7031
  build: async (buildOptions) => {
6866
7032
  const config = await buildCodexPluginThreadConfigWithinDeadline({
6867
7033
  ...buildParams,
7034
+ threadId: buildOptions?.threadId,
6868
7035
  appCache: appCache ?? defaultCodexAppInventoryCache,
6869
7036
  metadataCache,
6870
7037
  failClosedOnTimeout: Boolean(params.scheduledRuntimeAuthority),
@@ -6897,7 +7064,7 @@ function isCodexPluginThreadConfigTimeoutError(error) {
6897
7064
  * Startup orchestration for Codex app-server attempts, including shared-client
6898
7065
  * leasing, plugin thread config, sandbox environment, and thread lifecycle binding.
6899
7066
  */
6900
- const CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS = 3;
7067
+ const CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS = 3;
6901
7068
  const CODEX_APP_SERVER_CONTEXT_RESTART_SELECTION_CHANGED = "CODEX_APP_SERVER_CONTEXT_RESTART_SELECTION_CHANGED";
6902
7069
  /** True when a pre-write context restart must replay on the newly selected owner. */
6903
7070
  function isCodexContextRestartSelectionChangedError(error) {
@@ -7009,7 +7176,7 @@ async function startCodexAttemptThread(params) {
7009
7176
  signal: startupAbandonController.signal
7010
7177
  });
7011
7178
  } catch (error) {
7012
- if (startupAbandonController.signal.aborted) throw error;
7179
+ if (startupAbandonController.signal.aborted || isCodexAppServerStartSelectionChangedError(error)) throw error;
7013
7180
  throw new AgentHarnessPreflightError(`Codex Computer Use readiness failed: ${formatErrorMessage(error)}`, {
7014
7181
  cause: error,
7015
7182
  scope: "harness"
@@ -7023,7 +7190,8 @@ async function startCodexAttemptThread(params) {
7023
7190
  accountId: params.startupAuthAccountCacheKey,
7024
7191
  envApiKeyFingerprint: params.startupEnvApiKeyCacheKey,
7025
7192
  appServerVersion: activeStartupClient.getServerVersion(),
7026
- runtimeIdentity: startupRuntimeIdentity
7193
+ runtimeIdentity: startupRuntimeIdentity,
7194
+ desktopGenerationFingerprint: readCodexAppServerClientDesktopGenerationFingerprint(activeStartupClient)
7027
7195
  });
7028
7196
  const appServerRuntimeFingerprint = buildCodexAppServerRuntimeFingerprint({
7029
7197
  appServer: params.appServer,
@@ -7127,6 +7295,7 @@ async function startCodexAttemptThread(params) {
7127
7295
  finalConfigPatch: params.finalConfigPatch,
7128
7296
  buildFinalConfigPatch: params.buildFinalConfigPatch,
7129
7297
  nativeHookRelayGeneration: params.nativeHookRelayGeneration,
7298
+ nativeHookRelayRequired: params.nativeHookRelayRequired,
7130
7299
  nativeCodeModeEnabled: params.nativeToolSurfaceEnabled,
7131
7300
  nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
7132
7301
  nativeCodeModeOnlyEnabled: params.appServer.codeModeOnly,
@@ -7221,30 +7390,31 @@ async function startCodexAttemptThread(params) {
7221
7390
  }
7222
7391
  }
7223
7392
  };
7224
- for (let attempt = 1; attempt <= CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS; attempt += 1) try {
7393
+ for (let attempt = 1; attempt <= CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS; attempt += 1) try {
7225
7394
  return await startupAttempt();
7226
7395
  } catch (error) {
7227
7396
  const selectionChanged = isCodexAppServerStartSelectionChangedError(error);
7228
7397
  if (startupAbandoned || params.signal.aborted || !selectionChanged && !isCodexAppServerConnectionClosedError(error)) throw error;
7229
- const failedClient = attemptedClient;
7230
- const refreshedSharedClient = selectionChanged ? retireSharedCodexAppServerClientIfCurrent(failedClient) : clearSharedCodexAppServerClientIfCurrent(failedClient);
7231
- if (startupClientForAbandonedRequestCleanup === failedClient) startupClientForAbandonedRequestCleanup = void 0;
7232
- if (attempt >= CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS) {
7398
+ const refreshedSharedClient = selectionChanged ? retireSharedCodexAppServerClientIfCurrent(attemptedClient) : clearSharedCodexAppServerClientIfCurrent(attemptedClient);
7399
+ if (startupClientForAbandonedRequestCleanup === attemptedClient) startupClientForAbandonedRequestCleanup = void 0;
7400
+ if (attempt >= CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS) {
7233
7401
  embeddedAgentLog.warn(selectionChanged ? "codex app-server executable selection kept changing during startup; retries exhausted" : "codex app-server connection closed during startup; retries exhausted", {
7234
7402
  attempt,
7235
- maxAttempts: CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS,
7403
+ maxAttempts: CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS,
7236
7404
  refreshedSharedClient,
7237
7405
  error: formatErrorMessage(error)
7238
7406
  });
7239
7407
  throw error;
7240
7408
  }
7409
+ const retryDelayMs = selectionChanged ? 0 : 1e3 * 2 ** (attempt - 1);
7241
7410
  embeddedAgentLog.warn(selectionChanged ? "codex app-server executable selection changed during startup; restarting app-server and retrying" : "codex app-server connection closed during startup; restarting app-server and retrying", {
7242
7411
  attempt,
7243
7412
  nextAttempt: attempt + 1,
7244
- maxAttempts: CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS,
7413
+ maxAttempts: CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS,
7245
7414
  refreshedSharedClient,
7246
7415
  error: formatErrorMessage(error)
7247
7416
  });
7417
+ await sleepWithAbort(retryDelayMs, startupAbandonController.signal);
7248
7418
  }
7249
7419
  throw new Error("codex app-server startup retry loop exited unexpectedly");
7250
7420
  }
@@ -7257,7 +7427,7 @@ async function startCodexAttemptThread(params) {
7257
7427
  releaseSharedClientLease
7258
7428
  };
7259
7429
  } catch (error) {
7260
- if (params.signal.aborted || shouldClearSharedClientAfterStartupAbandon(error)) {
7430
+ if (params.signal.aborted || isCodexAppServerStartupError(error)) {
7261
7431
  releaseSharedClientLease?.();
7262
7432
  releaseSharedClientLease = void 0;
7263
7433
  await closeCodexStartupClientBestEffort(startupClientForAbandonedRequestCleanup);
@@ -7276,13 +7446,11 @@ async function startCodexAttemptThread(params) {
7276
7446
  params.signal.removeEventListener("abort", abandonStartupAcquire);
7277
7447
  }
7278
7448
  }
7279
- function shouldClearSharedClientAfterStartupAbandon(error) {
7280
- return isCodexAppServerStartupError(error);
7281
- }
7282
7449
  function shouldClearSharedClientAfterStartupRace(error) {
7283
- return shouldClearSharedClientAfterStartupAbandon(error) || isCodexAppServerRequestTimeoutError(error);
7450
+ return isCodexAppServerStartupError(error) || isCodexAppServerRequestTimeoutError(error);
7284
7451
  }
7285
7452
  function shouldClearSharedClientAfterStartupFailure(params) {
7453
+ if (isCodexAppServerOverloadError(params.error)) return false;
7286
7454
  return isCodexAppServerBrokenPipeError(params.error) || !params.spawnedBy;
7287
7455
  }
7288
7456
  //#endregion
@@ -7296,6 +7464,7 @@ async function startCodexAttemptRuntime(resources) {
7296
7464
  const developerInstructions = joinPresentSections(turnState.promptBuild.developerInstructions, attemptTools.scheduledConfiguredMcp?.diagnosticNotice);
7297
7465
  const { params, attemptClientFactory, bindingStore, appServer, pluginConfig, computerUseConfig, startupClientAuthProfileId, runtimeArtifactRequest, startupPreparedAuth, agentDir, sessionAgentId, effectiveWorkspace, effectiveCwd, sandbox, runAbortController, usesSupervisionConnection, resolveReviewerPolicyContext, resolveRuntimeOptionsForCurrentBinding, startupAuthProfileId, startupAuthRequirement, abortFromUpstream } = connection;
7298
7466
  let pluginAppServer = withCodexAppServerFastModeServiceTier(appServer, runtimeParams);
7467
+ const loopDetectionEnabled = (sessionAgentId && params.config ? resolveAgentConfig$1(params.config, sessionAgentId)?.tools?.loopDetection?.enabled : void 0) ?? params.config?.tools?.loopDetection?.enabled ?? false;
7299
7468
  try {
7300
7469
  emitCodexAppServerEvent(params, {
7301
7470
  stream: "codex_app_server.lifecycle",
@@ -7330,6 +7499,7 @@ async function startCodexAttemptRuntime(resources) {
7330
7499
  developerInstructions,
7331
7500
  agentWorkspaceDeveloperInstructions: context.agentWorkspaceDeveloperInstructions,
7332
7501
  buildFinalConfigPatch: buildNativeHookRelayFinalConfigPatch,
7502
+ nativeHookRelayRequired: connection.options.nativeHookRelay?.enabled !== false && params.pluginHarnessToolPolicyRestricted !== true && connection.nativeHookRelayEvents.includes("pre_tool_use") && (hasBeforeToolCallPolicy() || appServer.loopDetectionPreToolUseRelay && Boolean(connection.sandboxSessionKey) && loopDetectionEnabled),
7333
7503
  bundleMcpThreadConfig,
7334
7504
  configuredMcpOwnershipVersion: attemptTools.configuredMcpOwnershipVersion,
7335
7505
  nativeToolSurfaceEnabled,
@@ -7367,11 +7537,12 @@ async function startCodexAttemptRuntime(resources) {
7367
7537
  if (usesSupervisionConnection && (state.thread.connectionScope !== "supervision" || state.thread.supervisionSourceThreadId !== connection.mutable.startupBinding?.supervisionSourceThreadId)) throw new Error("Codex supervised thread lost its private connection ownership");
7368
7538
  if (state.thread.lifecycle.action === "started" || state.thread.lifecycle.action === "forked") {
7369
7539
  const activePolicy = resolveReviewerPolicyContext(state.thread);
7540
+ const activeConfig = resolveRuntimeOptionsForCurrentBinding({
7541
+ modelProvider: activePolicy.modelProvider,
7542
+ model: activePolicy.model
7543
+ });
7370
7544
  const activeAppServer = resolveCodexAppServerForModelProvider({
7371
- appServer: resolveRuntimeOptionsForCurrentBinding({
7372
- modelProvider: activePolicy.modelProvider,
7373
- model: activePolicy.model
7374
- }),
7545
+ appServer: activeConfig,
7375
7546
  provider: activePolicy.modelProvider,
7376
7547
  model: activePolicy.model,
7377
7548
  config: params.config,
@@ -7436,7 +7607,7 @@ function isAuthorityResolutionOperationAbort(error, signal) {
7436
7607
  }
7437
7608
  async function prepareCodexAttemptTools(runtime) {
7438
7609
  const { connection, bundleMcpThreadConfig, bundleManifestRegistry, runtimeParams, effectiveRuntimeModelId, nativeToolSurfaceEnabled, nativeProviderWebSearchSupport, hookChannelId, codexMcpToolOverrides, authenticatedScheduledMode, ownsScheduledConfiguredMcpSurface, canResolveScheduledConfiguredMcpCreatorAuthority } = runtime;
7439
- const { params, preDynamicStartupStages, mutable, startupAuthProfileId, resolvedWorkspace, effectiveWorkspace, effectiveCwd, sandboxSessionKey, sandbox, runAbortController, sessionAgentId, pluginConfig, profilerEnabled, agentDir } = connection;
7610
+ const { params, preDynamicStartupStages, mutable, startupAuthProfileId, resolvedWorkspace, effectiveWorkspace, effectiveCwd, sandboxSessionKey, contextSessionKey, sandbox, sessionPermissionPolicy, runAbortController, sessionAgentId, policyAgentId, pluginConfig, profilerEnabled, agentDir } = connection;
7440
7611
  const preDynamicSummary = preDynamicStartupStages.snapshot();
7441
7612
  if (shouldWarnCodexDynamicToolBuildStageSummary(preDynamicSummary)) embeddedAgentLog.warn(`codex app-server pre-dynamic startup timings runId=${params.runId} sessionId=${params.sessionId} totalMs=${preDynamicSummary.totalMs} stages=${formatCodexDynamicToolBuildStageSummary(preDynamicSummary)}`, {
7442
7613
  runId: params.runId,
@@ -7479,6 +7650,7 @@ async function prepareCodexAttemptTools(runtime) {
7479
7650
  ...onCodexToolOutcome ? { onToolOutcome: onCodexToolOutcome } : {}
7480
7651
  };
7481
7652
  const computerContextEpoch = { value: 0 };
7653
+ const runCleanups = [];
7482
7654
  const cronCreatorToolAllowlist = [];
7483
7655
  const cronCreatorToolAllowlistCaptureRef = {};
7484
7656
  const scheduledAppAuthoritySourceRef = {};
@@ -7509,10 +7681,12 @@ async function prepareCodexAttemptTools(runtime) {
7509
7681
  effectiveCwd,
7510
7682
  sandboxSessionKey,
7511
7683
  sandbox,
7684
+ sessionPermissionPolicy,
7512
7685
  nativeToolSurfaceEnabled,
7513
7686
  nativeProviderWebSearchSupport,
7514
7687
  runAbortController,
7515
7688
  sessionAgentId,
7689
+ policyAgentId,
7516
7690
  pluginConfig,
7517
7691
  profilerEnabled,
7518
7692
  ...params.cronCreatorAuthorityUnavailableReason === "queued-local-operator" && bundleMcpThreadConfig.staticServerNames.length > 0 ? { cronCreatorAuthorityUnavailableReason: "queued-local-operator-configured-mcp" } : {},
@@ -7539,6 +7713,7 @@ async function prepareCodexAttemptTools(runtime) {
7539
7713
  };
7540
7714
  const tools = await buildDynamicTools({
7541
7715
  ...commonToolParams,
7716
+ registerRunCleanup: (cleanup) => runCleanups.push(cleanup),
7542
7717
  cronCreatorToolAllowlistRef: cronCreatorToolAllowlist,
7543
7718
  cronCreatorToolAllowlistCaptureRef,
7544
7719
  onPersistentWebSearchPolicyResolved: (allowed) => {
@@ -7560,7 +7735,7 @@ async function prepareCodexAttemptTools(runtime) {
7560
7735
  runSessionKey: params.sessionKey && params.sessionKey !== sandboxSessionKey ? params.sessionKey : void 0,
7561
7736
  sessionId: params.sessionId,
7562
7737
  runId: params.runId,
7563
- agentId: sessionAgentId,
7738
+ agentId: policyAgentId,
7564
7739
  agentDir: agentDir ?? resolveAgentDir(params.config ?? {}, sessionAgentId),
7565
7740
  agentAccountId: params.agentAccountId,
7566
7741
  messageProvider: params.messageProvider ?? params.messageChannel,
@@ -7650,7 +7825,7 @@ async function prepareCodexAttemptTools(runtime) {
7650
7825
  remoteWorkspaceRoot: connection.appServer.remoteWorkspaceRoot,
7651
7826
  remoteWorkspaceRequestTimeoutMs: connection.appServer.requestTimeoutMs,
7652
7827
  sessionId: params.sessionId,
7653
- sessionKey: sandboxSessionKey,
7828
+ sessionKey: contextSessionKey,
7654
7829
  runId: params.runId,
7655
7830
  channelId: hookChannelId,
7656
7831
  currentChannelProvider: resolveCodexMessageToolProvider(params),
@@ -7759,6 +7934,7 @@ async function prepareCodexAttemptTools(runtime) {
7759
7934
  dynamicToolParams,
7760
7935
  compactionPlanState,
7761
7936
  computerContextEpoch,
7937
+ runCleanups,
7762
7938
  toolBridge,
7763
7939
  toolState,
7764
7940
  toolOutcomeOrdinals,
@@ -8240,8 +8416,10 @@ function createCodexAttemptTurnWatchController(params) {
8240
8416
  function scheduleAttemptIdleWatch() {
8241
8417
  scheduleWatch("attempt", fireAttemptIdleTimeout, attemptLastProgressAt, attemptIdleTimeoutOverrideMs ?? turnAttemptIdleTimeoutMs, attemptIdleWatchArmed && !shouldPauseAttemptIdleWatch());
8242
8418
  }
8419
+ const terminalWorkIsActive = () => params.getActiveAppServerTurnRequests() > 0 || params.isTerminalTurnNotificationQueued() && params.getActiveFinalizationHookCount() > 0;
8420
+ const terminalIdleTimeoutMs = () => params.isTerminalTurnNotificationQueued() && !terminalWorkIsActive() ? Math.min(TURN_TERMINAL_SETTLEMENT_IDLE_TIMEOUT_MS, turnTerminalIdleTimeoutMs) : turnTerminalIdleTimeoutMs;
8243
8421
  function scheduleTerminalIdleWatch() {
8244
- scheduleWatch("terminal", fireTerminalIdleTimeout, completionLastActivityAt, turnTerminalIdleTimeoutMs, terminalIdleWatchArmed && params.getActiveAppServerTurnRequests() === 0);
8422
+ scheduleWatch("terminal", fireTerminalIdleTimeout, completionLastActivityAt, terminalIdleTimeoutMs(), terminalIdleWatchArmed && (params.isTerminalTurnNotificationQueued() || params.getActiveAppServerTurnRequests() === 0));
8245
8423
  }
8246
8424
  function scheduleProgressWatches() {
8247
8425
  scheduleAttemptIdleWatch();
@@ -8362,18 +8540,26 @@ function createCodexAttemptTurnWatchController(params) {
8362
8540
  });
8363
8541
  }
8364
8542
  function fireTerminalIdleTimeout() {
8365
- if (params.isCompleted() || params.isTerminalTurnNotificationQueued() || params.signal.aborted || !terminalIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0) return;
8543
+ if (params.isCompleted() || params.signal.aborted || !terminalIdleWatchArmed) return;
8544
+ if (!params.isTerminalTurnNotificationQueued() && params.getActiveAppServerTurnRequests() > 0) {
8545
+ scheduleTerminalIdleWatch();
8546
+ return;
8547
+ }
8548
+ const timeoutMs = terminalIdleTimeoutMs();
8366
8549
  const idleMs = Math.max(0, Date.now() - completionLastActivityAt);
8367
- if (idleMs < turnTerminalIdleTimeoutMs) {
8550
+ if (idleMs < timeoutMs) {
8368
8551
  scheduleTerminalIdleWatch();
8369
8552
  return;
8370
8553
  }
8371
8554
  reportTimeout({
8372
8555
  kind: "terminal",
8373
8556
  idleMs,
8374
- timeoutMs: turnTerminalIdleTimeoutMs,
8557
+ timeoutMs,
8375
8558
  lastActivityReason: completionLastActivityReason,
8376
- details: completionLastActivityDetails
8559
+ details: {
8560
+ ...completionLastActivityDetails,
8561
+ terminalTurnNotificationQueued: params.isTerminalTurnNotificationQueued()
8562
+ }
8377
8563
  });
8378
8564
  }
8379
8565
  return {
@@ -8430,6 +8616,7 @@ function createCodexAttemptTurnWatchController(params) {
8430
8616
  completionLastActivityReason = `notification:${method}`;
8431
8617
  if (options?.details !== void 0) completionLastActivityDetails = options.details;
8432
8618
  if (options?.attemptProgress) recordAttemptProgress(completionLastActivityReason, options);
8619
+ if (params.isTerminalTurnNotificationQueued()) scheduleTerminalIdleWatch();
8433
8620
  },
8434
8621
  extendAttemptIdleWatch: (timeoutMs) => {
8435
8622
  attemptIdleTimeoutOverrideMs = resolveWatchTimeoutMs(timeoutMs);
@@ -8452,6 +8639,8 @@ function createCodexAttemptTurnState(resources) {
8452
8639
  latestStartupErrorNotification: void 0,
8453
8640
  rateLimitsRevisionBeforeLastTurnStart: void 0,
8454
8641
  completed: false,
8642
+ abortCleanup: Promise.resolve(),
8643
+ permissionChangeRestart: void 0,
8455
8644
  localCompletionRequested: false,
8456
8645
  terminalTurnNotificationQueued: false,
8457
8646
  sawCodexInterruptMarker: false,