@openclaw/codex 2026.8.1-beta.3 → 2026.8.1

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 (64) hide show
  1. package/dist/api.js +4 -5
  2. package/dist/{attempt-client-cleanup-URjUlCHg.js → attempt-client-cleanup-CVPvp9dL.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-CCQFs8lf.js} +2 -2
  5. package/dist/{bounded-turn-4BjrZEwW.js → bounded-turn-rq5tiSXk.js} +13 -18
  6. package/dist/{capabilities-CH3K57v4.js → capabilities-CGm_94eL.js} +1 -1
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-D0nZ3qtd.js → command-formatters-C54Yg3eA.js} +2 -2
  9. package/dist/{command-handlers-DC86lgT4.js → command-handlers-BvOAHTrA.js} +89 -62
  10. package/dist/{compact-COqgBRny.js → compact-Hd7SAc0N.js} +8 -9
  11. package/dist/{config-CpNeo5AE.js → config-Cxw3Cl3k.js} +346 -286
  12. package/dist/doctor-contract-api.js +115 -14
  13. package/dist/{dynamic-tools-fJZhl4CE.js → dynamic-tools-BFoUcRdx.js} +147 -903
  14. package/dist/{effective-mcp-catalog-4YKn51aG.js → effective-mcp-catalog-BTJHnDSx.js} +2 -2
  15. package/dist/{event-projector-assistant-message-BwxV4d6N.js → event-projector-assistant-message-C5-Ds8mA.js} +18 -1
  16. package/dist/{event-projector-usage-CLwNEJqW.js → event-projector-usage-D3PHFFXh.js} +3 -3
  17. package/dist/{harness-DoKQcF8n.js → harness-DVfUQBlQ.js} +42 -52
  18. package/dist/harness.js +1 -1
  19. package/dist/index.js +278 -295
  20. package/dist/{isolated-completion-DPMtlH9r.js → isolated-completion-Cbbx_5Ed.js} +4 -4
  21. package/dist/launch-args-sXOAg0SX.js +84 -0
  22. package/dist/{managed-binary-By4FZC6i.js → managed-binary-ZkGodW19.js} +43 -7
  23. package/dist/media-understanding-provider.js +1 -1
  24. package/dist/model-catalog-o6Cgjoss.js +101 -0
  25. package/dist/{models-CZw1wClT.js → models-DgFWSuxA.js} +25 -28
  26. package/dist/{native-subagent-monitor-GGq3RnOo.js → native-subagent-monitor-CwFyoylz.js} +207 -30
  27. package/dist/{node-exec-server.runtime-Bsu1LdFZ.js → node-exec-server.runtime-DXrqpw1c.js} +8 -5
  28. package/dist/{plugin-approval-roundtrip-C75Yiiwc.js → plugin-approval-roundtrip-D80UZiJ_.js} +1 -1
  29. package/dist/{plugin-marketplace-discovery-CSWDkMrP.js → plugin-marketplace-discovery-mdmScYOj.js} +30 -39
  30. package/dist/{plugin-metadata-cache-BXH5NF-4.js → plugin-metadata-cache-CulOABwu.js} +162 -135
  31. package/dist/{protocol-validators-VAVyEnDp.js → protocol-validators-Bg5rpNxk.js} +148 -5
  32. package/dist/{rate-limits-BI1dMHDG.js → rate-limits-hFtzt2Up.js} +1 -1
  33. package/dist/{sandbox-guard-C1qEeZ5C.js → request-CCm4LaED.js} +183 -2
  34. package/dist/{run-attempt-BV3HzuCf.js → run-attempt-BwNQDH2u.js} +599 -361
  35. package/dist/{runtime-artifact-BvyT1uyd.js → runtime-artifact-DeBvleJw.js} +1 -1
  36. package/dist/{session-binding-BTrLf9Qm.js → session-binding-q2WSTVmC.js} +195 -122
  37. package/dist/{session-catalog-ezP_UcRM.js → session-catalog-C2Tae4rI.js} +229 -122
  38. package/dist/session-catalog-home-id-DAKUrm3C.js +112 -0
  39. package/dist/{session-cli-B1hKvfkI.js → session-cli-BuDjm5Ou.js} +1 -1
  40. package/dist/session-permission-policy-gLm40Vtq.js +160 -0
  41. package/dist/session-retirement-CdkI0bvb.js +90 -0
  42. package/dist/{session-upstream-marker-D_2npHlA.js → session-upstream-marker-Bcy33kam.js} +2 -3
  43. package/dist/{settled-turn-finalizer-Dx1onIqg.js → settled-turn-finalizer-B8JJcxCi.js} +7 -6
  44. package/dist/{shared-client-Dw-rzii0.js → shared-client-kNkljY-R.js} +1720 -786
  45. package/dist/{side-question-DbXkJZzg.js → side-question-GwD73tX6.js} +69 -39
  46. package/dist/{thread-lifecycle-DKdrnTMX.js → thread-lifecycle-Ss-uwnC4.js} +852 -359
  47. package/dist/{thread-ownership-i2NBwDUL.js → thread-ownership-CeYBqg_4.js} +18 -8
  48. package/dist/{plugin-app-cache-key-DMNQ8Zi8.js → thread-resume-DF54hagX.js} +52 -6
  49. package/dist/{transcript-mirror-BU0_LKAL.js → transcript-mirror-CkEQIna-.js} +112 -13
  50. package/dist/transport-stdio-C7-s9TLG.js +708 -0
  51. package/dist/{upstream-session-fork-BDVJCLAD.js → upstream-session-fork-BACGlOFa.js} +78 -86
  52. package/dist/{usage-9dKJCgcy.js → usage-C2l8Mils.js} +3 -3
  53. package/dist/{web-search-provider.runtime-DCdx4DGu.js → web-search-provider.runtime-B2XgRdFv.js} +3 -3
  54. package/openclaw.plugin.json +19 -1
  55. package/package.json +8 -8
  56. package/dist/app-server-policy-bg5QPSVP.js +0 -25
  57. package/dist/command-rpc-xUIIonsO.js +0 -90
  58. package/dist/incognito-session-ET_ufDH-.js +0 -2
  59. package/dist/model-catalog-CmGoqVKB.js +0 -48
  60. package/dist/request-DSK0W6YI.js +0 -157
  61. package/dist/session-catalog-home-id-BNwAJ9D7.js +0 -18
  62. package/dist/session-retirement-CE4iMiLh.js +0 -36
  63. package/dist/thread-resume-CdJk2Ehf.js +0 -30
  64. package/dist/transport-stdio-DhoGy33Z.js +0 -99
@@ -1,42 +1,46 @@
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";
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-Cxw3Cl3k.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-kNkljY-R.js";
3
+ import { c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, i as createCodexSessionGenerationSupersededError, l as resolveCodexRunSessionBindingAuthority, u as scopeCodexRunBindingStore } from "./session-binding-q2WSTVmC.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-CVPvp9dL.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-D3PHFFXh.js";
7
+ import { i as buildCodexPluginAppCacheKey, r as buildCodexAppServerRuntimeFingerprint, s as defaultCodexAppInventoryCache } from "./thread-resume-DF54hagX.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-Ss-uwnC4.js";
9
+ import { l as isIncognitoSessionKey, r as retainCodexAppServerBindingSubscription } from "./thread-ownership-CeYBqg_4.js";
10
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";
11
+ import { a as buildCodexUserPromptMessage, i as mirrorPromptAtTurnStartBestEffort, l as readCodexMirrorSourceFingerprint, n as createCodexAppServerUserMessagePersistenceNotifier, o as promptSnapshot, s as applyCodexTranscriptTaint, t as codexTranscriptMirrorRuntime, u as serializeCodexMirrorSourceEvidence } from "./transcript-mirror-CkEQIna-.js";
12
+ import { t as CODEX_CONTROL_METHODS } from "./capabilities-CGm_94eL.js";
13
+ import { a as shouldRefreshCodexRateLimitsForUsageLimitMessage, i as resolveCodexUsageLimitResetAtMs, n as formatCodexUsageLimitErrorMessage } from "./rate-limits-hFtzt2Up.js";
14
+ import { r as formatCodexDisplayText } from "./command-formatters-C54Yg3eA.js";
15
+ import { a as resolveCodexAppServerForModelProvider, i as resolveCodexSessionPermissionCwd, n as applyCodexSessionPermissionPolicy, r as resolveCodexEffectiveSessionPermissionPolicy } from "./session-permission-policy-gLm40Vtq.js";
16
+ import { n as resolveCodexBindingAppServerConnection } from "./binding-connection-CCQFs8lf.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 readCodexMirroredSessionHistoryMessages, c as inferCodexDynamicToolMeta, d as sanitizeCodexToolArguments, f as sanitizeCodexToolResponse, i as CodexToolTranscriptProjection, l as isCodexCommandBearingToolCall, n as CodexEventProjection, o as CodexToolProgressProjection, r as emitCodexAgentEvent, s as shouldEmitTranscriptToolProgress, t as codexNativeSubagentMonitorRuntime, u as resolveCodexToolProgressDetailMode } from "./native-subagent-monitor-CwFyoylz.js";
21
+ 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-BFoUcRdx.js";
22
+ import { a as runCodexComputerUseLiveTest, n as ensureCodexComputerUse, t as defaultCodexPluginMetadataCache } from "./plugin-metadata-cache-CulOABwu.js";
23
23
  import { asBoolean, asFiniteNumber, asOptionalRecord, hasNonEmptyString, normalizeOptionalString, readNonBlankString, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
24
24
  import fs from "node:fs/promises";
25
25
  import path from "node:path";
26
26
  import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
27
27
  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";
28
+ 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
29
  import crypto, { createHash } from "node:crypto";
30
- import { hostname } from "node:os";
31
30
  import { markAuthProfileBlockedUntil, resolveAgentDir as resolveAgentDir$1, resolveAgentWorkspaceDir } from "openclaw/plugin-sdk/agent-runtime";
31
+ import { resolveAgentConfig as resolveAgentConfig$1, resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
32
32
  import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
33
33
  import { parseSqliteSessionFileMarker } from "openclaw/plugin-sdk/session-store-runtime";
34
+ import { addAbortListener } from "node:events";
34
35
  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
36
  import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent, emitTrustedDiagnosticEventWithPrivateData, freezeDiagnosticTraceContext, onInternalDiagnosticEvent, resolveDiagnosticModelContentCapturePolicy } from "openclaw/plugin-sdk/diagnostic-runtime";
37
+ import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
38
+ import { captureFinalCodexCronCreatorToolAllowlist, materializeStaticMcpToolsForScheduledHarnessRun, resolveCodexMcpToolOverridesForAgent } from "openclaw/plugin-sdk/codex-mcp-projection";
39
+ import { hasPromptImageInput } from "openclaw/plugin-sdk/session-transcript-runtime";
38
40
  import { Buffer as Buffer$1 } from "node:buffer";
41
+ import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
39
42
  import { prepareMemorySystemPromptAddition } from "openclaw/plugin-sdk/core";
43
+ import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-local-roots";
40
44
  import { isSilentReplyPayloadText } from "openclaw/plugin-sdk/reply-chunking";
41
45
  import { generatedImageAssetFromBase64 } from "openclaw/plugin-sdk/image-generation";
42
46
  import { resolveGeneratedMediaMaxBytes } from "openclaw/plugin-sdk/media-generation-runtime";
@@ -68,6 +72,12 @@ function createCodexSteeringQueue(params) {
68
72
  let sendChain = Promise.resolve();
69
73
  let sealedError;
70
74
  let closedError;
75
+ const assertActive = () => {
76
+ const unavailableError = closedError ?? sealedError;
77
+ if (unavailableError) throw unavailableError;
78
+ params.signal.throwIfAborted();
79
+ params.assertActive();
80
+ };
71
81
  const clearBatchTimer = () => {
72
82
  if (batchTimer) {
73
83
  clearTimeout(batchTimer);
@@ -120,19 +130,20 @@ function createCodexSteeringQueue(params) {
120
130
  const sendBatch = async (items) => {
121
131
  const liveItems = items.filter((item) => !item.settled);
122
132
  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);
133
+ let clientUserMessageId;
131
134
  try {
135
+ assertActive();
136
+ const input = [];
137
+ for (const item of liveItems) {
138
+ input.push(...await params.prepareMessage(item.text, item));
139
+ assertActive();
140
+ }
141
+ clientUserMessageId = `openclaw:${params.turnId}:steer:${++batchSequence}`;
142
+ dispatchedBatches.set(clientUserMessageId, { items: liveItems });
132
143
  await params.client.request("turn/steer", {
133
144
  threadId: params.threadId,
134
145
  expectedTurnId: params.turnId,
135
- input: liveItems.flatMap((item) => buildCodexUserInput(item.text, item.images)),
146
+ input,
136
147
  clientUserMessageId
137
148
  }, {
138
149
  timeoutMs: params.requestTimeoutMs,
@@ -140,8 +151,8 @@ function createCodexSteeringQueue(params) {
140
151
  });
141
152
  for (const item of liveItems) reportItemAcceptance(item, true);
142
153
  } catch (error) {
143
- dispatchedBatches.delete(clientUserMessageId);
144
- const acceptedUnconfirmed = isCodexAppServerIndeterminateRequestCancellationError(error) || isCodexAppServerIndeterminateTransportError(error);
154
+ if (clientUserMessageId) dispatchedBatches.delete(clientUserMessageId);
155
+ const acceptedUnconfirmed = clientUserMessageId !== void 0 && (isCodexAppServerIndeterminateRequestCancellationError(error) || isCodexAppServerIndeterminateTransportError(error));
145
156
  for (const item of liveItems) {
146
157
  if (acceptedUnconfirmed) reportItemAcceptance(item, true);
147
158
  rejectItem(item, error);
@@ -175,15 +186,12 @@ function createCodexSteeringQueue(params) {
175
186
  rejectDelivery = reject;
176
187
  });
177
188
  const item = {
189
+ ...options,
178
190
  acceptance: "open",
179
191
  text,
180
- images: options?.images,
181
- isInboundUserMessage: options?.isInboundUserMessage,
182
- onQueueAccepted: options?.onQueueAccepted,
183
192
  resolve: resolveDelivery,
184
193
  reject: rejectDelivery,
185
- settled: false,
186
- userTurnTranscriptRecorder: options?.userTurnTranscriptRecorder
194
+ settled: false
187
195
  };
188
196
  pendingMessages.add(item);
189
197
  return {
@@ -195,10 +203,11 @@ function createCodexSteeringQueue(params) {
195
203
  if (params.signal.aborted) abortQueue();
196
204
  return {
197
205
  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) {
206
+ try {
207
+ assertActive();
208
+ } catch (error) {
200
209
  options?.onQueueAccepted?.(false);
201
- throw unavailableError;
210
+ throw error;
202
211
  }
203
212
  const { item, delivery } = createPendingMessage(text, options);
204
213
  batchedMessages.push(item);
@@ -242,15 +251,17 @@ function normalizeCodexSteerDebounceMs(value) {
242
251
  //#endregion
243
252
  //#region extensions/codex/src/app-server/event-projector-assistant.ts
244
253
  var CodexAssistantProjection = class {
245
- constructor(params, emitAgentEvent, matchesToolProgressEcho, nextTranscriptTimestamp) {
254
+ constructor(params, emitAgentEvent, matchesToolProgressEcho, nextTranscriptTimestamp, checkpointCommentary) {
246
255
  this.params = params;
247
256
  this.emitAgentEvent = emitAgentEvent;
248
257
  this.matchesToolProgressEcho = matchesToolProgressEcho;
249
258
  this.nextTranscriptTimestamp = nextTranscriptTimestamp;
259
+ this.checkpointCommentary = checkpointCommentary;
250
260
  this.assistantTextByItem = /* @__PURE__ */ new Map();
251
261
  this.assistantItemOrder = [];
252
262
  this.assistantTimestampByItem = /* @__PURE__ */ new Map();
253
263
  this.assistantPhaseByItem = /* @__PURE__ */ new Map();
264
+ this.assistantDeliveryByItem = /* @__PURE__ */ new Map();
254
265
  this.latestTerminalAssistantCandidateSuperseded = false;
255
266
  this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
256
267
  this.terminalAssistantCandidateEarlierActiveItemIds = /* @__PURE__ */ new Set();
@@ -261,7 +272,9 @@ var CodexAssistantProjection = class {
261
272
  this.assistantStarted = false;
262
273
  this.streamedPartialAssistantItemReplaceable = false;
263
274
  this.persistableAssistantBarrier = 0;
275
+ this.persistedAssistantBoundary = false;
264
276
  this.persistableAssistantBarrierItemIds = /* @__PURE__ */ new Set();
277
+ this.completedAssistantItemIds = /* @__PURE__ */ new Set();
265
278
  }
266
279
  hasCompletedTerminalAssistantText(completedItemIds) {
267
280
  const latestCompletedItemId = this.latestCompletedTerminalAssistantItemId;
@@ -293,7 +306,8 @@ var CodexAssistantProjection = class {
293
306
  if (!delta) return;
294
307
  if (itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
295
308
  const isCommentary = this.isCommentaryAssistantItem(itemId);
296
- if (!isCommentary && itemId !== this.latestTerminalAssistantCandidateItemId) this.markTerminalAssistantCandidateSupersededBy();
309
+ const isAsync = this.isAsyncAssistantItem(itemId);
310
+ if (!isCommentary && !isAsync && itemId !== this.latestTerminalAssistantCandidateItemId) this.markTerminalAssistantCandidateSupersededBy();
297
311
  if (!this.assistantStarted) {
298
312
  this.assistantStarted = true;
299
313
  await this.params.onAssistantMessageStart?.();
@@ -301,6 +315,7 @@ var CodexAssistantProjection = class {
301
315
  this.rememberAssistantItem(itemId);
302
316
  const text = `${this.assistantTextByItem.get(itemId) ?? ""}${delta}`;
303
317
  this.assistantTextByItem.set(itemId, text);
318
+ if (isAsync) return;
304
319
  if (isCommentary) {
305
320
  this.emitCommentaryProgress({
306
321
  itemId,
@@ -332,24 +347,25 @@ var CodexAssistantProjection = class {
332
347
  }
333
348
  recordItemStarted(item, itemId) {
334
349
  this.noteNativeWorkBarrier(item);
335
- if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
336
350
  this.rememberAssistantPhase(item);
351
+ if (item?.type === "agentMessage" && itemId && !this.isNonTerminalAssistantItem(itemId) && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
337
352
  if (item?.type === "agentMessage" && itemId) this.rememberAssistantItem(itemId);
338
- if (itemId && itemId !== this.latestTerminalAssistantCandidateItemId) {
353
+ if (itemId && !this.isNonTerminalAssistantItem(itemId) && itemId !== this.latestTerminalAssistantCandidateItemId) {
339
354
  this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
340
355
  if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
341
356
  }
342
357
  }
343
358
  recordItemCompleted(item, itemId, activeItemIds) {
359
+ if (itemId && item?.type === "agentMessage") this.completedAssistantItemIds.add(itemId);
344
360
  this.noteNativeWorkBarrier(item);
345
- if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
346
- if (itemId) this.latestCompletedItemId = itemId;
347
361
  this.rememberAssistantPhase(item);
348
- if (item?.type === "agentMessage" && !this.isCommentaryAssistantItem(item.id)) {
362
+ if (item?.type === "agentMessage" && itemId && !this.isNonTerminalAssistantItem(itemId) && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
363
+ if (itemId && !this.isNonTerminalAssistantItem(itemId)) this.latestCompletedItemId = itemId;
364
+ if (item?.type === "agentMessage" && !this.isNonTerminalAssistantItem(item.id)) {
349
365
  this.latestCompletedTerminalAssistantItemId = item.id;
350
366
  this.markLatestTerminalAssistantCandidate(item.id, activeItemIds);
351
367
  this.pendingRawTerminalAssistantEchoItemId = item.id;
352
- } else if (itemId) {
368
+ } else if (itemId && !this.isNonTerminalAssistantItem(itemId)) {
353
369
  this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
354
370
  if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
355
371
  }
@@ -362,14 +378,17 @@ var CodexAssistantProjection = class {
362
378
  text: item.text
363
379
  });
364
380
  this.pendingRawCommentaryEchoes += 1;
365
- } else if (item.text && this.isFinalAnswerAssistantItem(item.id)) this.emitAnswerCandidate(item.id, "candidate");
381
+ } else if (item.text && !this.isAsyncAssistantItem(item.id) && this.isFinalAnswerAssistantItem(item.id)) this.emitAnswerCandidate(item.id, "candidate");
382
+ return this.createAsyncDelivery(item.id);
366
383
  }
367
384
  }
368
385
  recordSnapshotItem(item) {
386
+ if (item.type === "agentMessage") this.completedAssistantItemIds.add(item.id);
369
387
  this.rememberAssistantPhase(item);
370
388
  if (item.type === "agentMessage" && typeof item.text === "string") {
371
389
  this.rememberAssistantItem(item.id);
372
390
  this.assistantTextByItem.set(item.id, item.text);
391
+ return this.createAsyncDelivery(item.id);
373
392
  }
374
393
  }
375
394
  handleRawResponseItemCompleted(item, activeItemIds) {
@@ -403,6 +422,7 @@ var CodexAssistantProjection = class {
403
422
  const isIdlessTerminalAssistantAfterCompletedWork = candidateWasSupersededBeforeRaw && rawItemId === void 0 && pendingTerminalAssistantEchoItemId === void 0 && activeItemIds.size === 0;
404
423
  if (text && phase !== "commentary" && candidateWasSupersededBeforeRaw && itemId !== this.streamedPartialAssistantItemId && !isIdlessTerminalAssistantAfterCompletedWork) return;
405
424
  if (phase) this.assistantPhaseByItem.set(itemId, phase);
425
+ this.completedAssistantItemIds.add(itemId);
406
426
  this.rememberAssistantItem(itemId);
407
427
  this.assistantTextByItem.set(itemId, text);
408
428
  if (!text) return;
@@ -418,6 +438,7 @@ var CodexAssistantProjection = class {
418
438
  const audibleAfterHandoff = afterHandoff.filter((text) => !isSilentReplyPayloadText(text));
419
439
  if (audibleAfterHandoff.length > 0) return audibleAfterHandoff;
420
440
  if (afterHandoff.length > 0) return afterHandoff.slice(-1);
441
+ if (this.persistedAssistantBoundary) return [];
421
442
  const recoveredAudible = this.collectPersistableAssistantTexts(0).filter((text) => !isSilentReplyPayloadText(text));
422
443
  if (recoveredAudible.length > 0) return recoveredAudible.slice(-1);
423
444
  const recovered = this.resolveFinalAssistantTextItem()?.text;
@@ -425,16 +446,52 @@ var CodexAssistantProjection = class {
425
446
  }
426
447
  collectCommentaryMessages() {
427
448
  return this.assistantItemOrder.flatMap((itemId) => {
428
- if (!this.isCommentaryAssistantItem(itemId)) return [];
449
+ const message = this.readCommentaryMessage(itemId);
450
+ return message ? [{
451
+ itemId,
452
+ message
453
+ }] : [];
454
+ });
455
+ }
456
+ readCommentaryMessage(itemId) {
457
+ const text = this.assistantTextByItem.get(itemId)?.trim();
458
+ const timestamp = this.assistantTimestampByItem.get(itemId);
459
+ return this.isCommentaryAssistantItem(itemId) && text && timestamp !== void 0 ? createAssistantCommentaryMessage(this.params, text, itemId, timestamp) : void 0;
460
+ }
461
+ collectAsyncMessages() {
462
+ return this.assistantItemOrder.flatMap((itemId) => {
463
+ if (!this.isAsyncAssistantItem(itemId)) return [];
429
464
  const text = this.assistantTextByItem.get(itemId)?.trim();
430
465
  const timestamp = this.assistantTimestampByItem.get(itemId);
431
466
  if (!text || timestamp === void 0) return [];
432
467
  return [{
433
468
  itemId,
434
- message: createAssistantCommentaryMessage(this.params, text, itemId, timestamp)
469
+ message: createAssistantAsyncMessage(this.params, text, itemId, timestamp)
470
+ }];
471
+ });
472
+ }
473
+ collectCompletedAssistantMessages(completedItemIds, options) {
474
+ return this.assistantItemOrder.flatMap((itemId) => {
475
+ const text = this.assistantTextByItem.get(itemId)?.trim();
476
+ if (!completedItemIds.has(itemId) || this.isNonTerminalAssistantItem(itemId) || !text || isSilentReplyPayloadText(text) || this.isToolProgressEchoText(itemId, text)) return [];
477
+ const message = this.createAssistantMessage(text, options);
478
+ const timestamp = this.assistantTimestampByItem.get(itemId) ?? message.timestamp;
479
+ return [{
480
+ itemId,
481
+ message: {
482
+ ...message,
483
+ timestamp
484
+ }
435
485
  }];
436
486
  });
437
487
  }
488
+ markAssistantBoundaryPersisted(itemId) {
489
+ const index = this.assistantItemOrder.indexOf(itemId);
490
+ if (index >= 0) {
491
+ this.persistableAssistantBarrier = Math.max(this.persistableAssistantBarrier, index + 1);
492
+ this.persistedAssistantBoundary = true;
493
+ }
494
+ }
438
495
  finalizeAnswerCandidate(turn) {
439
496
  if (turn.status !== "completed") {
440
497
  this.supersedeVisibleAnswerCandidate();
@@ -444,7 +501,7 @@ var CodexAssistantProjection = class {
444
501
  const authoritativeIndex = turnItems.findLastIndex((item) => {
445
502
  if (item.type !== "agentMessage" || typeof item.text !== "string" || item.text.trim().length === 0) return false;
446
503
  const phase = readItemString(item, "phase");
447
- return phase === "final_answer" || phase === void 0;
504
+ return readItemString(item, "delivery") !== "async" && (phase === "final_answer" || phase === void 0);
448
505
  });
449
506
  const authoritative = authoritativeIndex >= 0 ? turnItems[authoritativeIndex] : void 0;
450
507
  if (turnItems.slice(authoritativeIndex + 1).some(shouldClearTerminalPresentationForNativeItem) || authoritative?.id === this.latestTerminalAssistantCandidateItemId && this.latestTerminalAssistantCandidateSuperseded) {
@@ -462,7 +519,7 @@ var CodexAssistantProjection = class {
462
519
  hasAssistantItemTextForSynthesis() {
463
520
  for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
464
521
  const itemId = this.assistantItemOrder[i];
465
- if (!itemId || this.assistantPhaseByItem.get(itemId) === "commentary") continue;
522
+ if (!itemId || this.isNonTerminalAssistantItem(itemId)) continue;
466
523
  const text = this.assistantTextByItem.get(itemId);
467
524
  if (text && text.length > 0) return true;
468
525
  }
@@ -471,7 +528,7 @@ var CodexAssistantProjection = class {
471
528
  createCurrentAttemptAssistantMessage(options) {
472
529
  for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
473
530
  const itemId = this.assistantItemOrder[i];
474
- if (!itemId || this.isCommentaryAssistantItem(itemId) || !this.assistantTextByItem.has(itemId)) continue;
531
+ if (!itemId || this.isNonTerminalAssistantItem(itemId) || !this.assistantTextByItem.has(itemId)) continue;
475
532
  const text = this.assistantTextByItem.get(itemId) ?? "";
476
533
  const normalizedText = text.trim();
477
534
  if (normalizedText && this.isToolProgressEchoText(itemId, normalizedText)) continue;
@@ -492,10 +549,18 @@ var CodexAssistantProjection = class {
492
549
  if (item?.type !== "agentMessage") return;
493
550
  const phase = readItemString(item, "phase");
494
551
  if (phase) this.assistantPhaseByItem.set(item.id, phase);
552
+ const delivery = readItemString(item, "delivery");
553
+ if (delivery) this.assistantDeliveryByItem.set(item.id, delivery);
495
554
  }
496
555
  isCommentaryAssistantItem(itemId) {
497
556
  return this.assistantPhaseByItem.get(itemId) === "commentary";
498
557
  }
558
+ isAsyncAssistantItem(itemId) {
559
+ return this.assistantDeliveryByItem.get(itemId) === "async";
560
+ }
561
+ isNonTerminalAssistantItem(itemId) {
562
+ return this.isCommentaryAssistantItem(itemId) || this.isAsyncAssistantItem(itemId);
563
+ }
499
564
  isFinalAnswerAssistantItem(itemId) {
500
565
  return this.assistantPhaseByItem.get(itemId) === "final_answer";
501
566
  }
@@ -503,7 +568,7 @@ var CodexAssistantProjection = class {
503
568
  return this.assistantPhaseByItem.get(itemId) === "final_answer";
504
569
  }
505
570
  emitCommentaryProgress(params) {
506
- const progressText = params.text.replace(/\s+/g, " ").trim();
571
+ const progressText = params.text.trim();
507
572
  if (!progressText || this.lastCommentaryProgressTextByItem.get(params.itemId) === progressText) return;
508
573
  this.lastCommentaryProgressTextByItem.set(params.itemId, progressText);
509
574
  this.emitAgentEvent({
@@ -570,7 +635,7 @@ var CodexAssistantProjection = class {
570
635
  const itemId = this.assistantItemOrder[i];
571
636
  if (!itemId) continue;
572
637
  const text = this.assistantTextByItem.get(itemId)?.trim();
573
- if (this.assistantPhaseByItem.get(itemId) === "commentary") continue;
638
+ if (this.isNonTerminalAssistantItem(itemId)) continue;
574
639
  if (text && !this.isToolProgressEchoText(itemId, text)) return {
575
640
  itemId,
576
641
  text
@@ -582,7 +647,7 @@ var CodexAssistantProjection = class {
582
647
  let replaceable = false;
583
648
  for (let index = minIndex; index < this.assistantItemOrder.length; index += 1) {
584
649
  const itemId = this.assistantItemOrder[index];
585
- if (!itemId || this.assistantPhaseByItem.get(itemId) === "commentary") continue;
650
+ if (!itemId || this.isNonTerminalAssistantItem(itemId)) continue;
586
651
  const text = this.assistantTextByItem.get(itemId)?.trim();
587
652
  if (!text || this.isToolProgressEchoText(itemId, text)) continue;
588
653
  if (isSilentReplyPayloadText(text)) {
@@ -606,9 +671,26 @@ var CodexAssistantProjection = class {
606
671
  this.persistableAssistantBarrier = this.assistantItemOrder.length;
607
672
  }
608
673
  rememberAssistantItem(itemId) {
609
- if (!itemId || this.assistantItemOrder.includes(itemId)) return;
610
- this.assistantItemOrder.push(itemId);
611
- this.assistantTimestampByItem.set(itemId, this.nextTranscriptTimestamp());
674
+ if (!itemId) return;
675
+ if (!this.assistantTimestampByItem.has(itemId)) {
676
+ this.assistantItemOrder.push(itemId);
677
+ this.assistantTimestampByItem.set(itemId, this.nextTranscriptTimestamp());
678
+ }
679
+ if (this.isCommentaryAssistantItem(itemId)) this.checkpointCommentary?.(itemId, {
680
+ read: () => this.readCommentaryMessage(itemId),
681
+ ready: () => this.completedAssistantItemIds.has(itemId)
682
+ });
683
+ }
684
+ createAsyncDelivery(itemId) {
685
+ if (!this.isAsyncAssistantItem(itemId)) return;
686
+ const text = this.assistantTextByItem.get(itemId);
687
+ const timestamp = this.assistantTimestampByItem.get(itemId);
688
+ if (!text?.trim() || timestamp === void 0) return;
689
+ return {
690
+ itemId,
691
+ message: createAssistantAsyncMessage(this.params, text, itemId, timestamp),
692
+ text
693
+ };
612
694
  }
613
695
  isToolProgressEchoText(itemId, text) {
614
696
  return this.rawPromotedAssistantItemIds.has(itemId) && this.matchesToolProgressEcho(text);
@@ -618,6 +700,40 @@ function shouldAdvancePersistableAssistantBarrier(item) {
618
700
  return shouldClearTerminalPresentationForNativeItem(item) || item.type === "dynamicToolCall" || item.type === "sleep";
619
701
  }
620
702
  //#endregion
703
+ //#region extensions/codex/src/app-server/event-projector-async-delivery.ts
704
+ /** Owns authorization, exactly-once settlement, and terminal retry for async user messages. */
705
+ var CodexAsyncDeliveryProjection = class {
706
+ constructor(params, threadId, turnId, options) {
707
+ this.params = params;
708
+ this.threadId = threadId;
709
+ this.turnId = turnId;
710
+ this.options = options;
711
+ this.settledItemIds = /* @__PURE__ */ new Set();
712
+ this.pendingDeliveries = /* @__PURE__ */ new Map();
713
+ }
714
+ allows(item, reportUnauthorized = false) {
715
+ if (item?.type !== "agentMessage" || item.delivery !== "async") return true;
716
+ const allowed = this.params.disableTools !== true && (this.options.asyncUserMessageAllowed ?? (this.params.toolsAllow === void 0 || this.params.toolsAllow.some((name) => name === "*" || name === "message")));
717
+ if (!allowed && reportUnauthorized) embeddedAgentLog.warn("blocked unauthorized codex async user message", {
718
+ itemId: item.id,
719
+ threadId: this.threadId,
720
+ turnId: this.turnId
721
+ });
722
+ return allowed;
723
+ }
724
+ pending() {
725
+ return [...this.pendingDeliveries.values()];
726
+ }
727
+ async deliver(delivery) {
728
+ if (this.settledItemIds.has(delivery.itemId)) return;
729
+ const settlement = await this.options.onAsyncDelivery?.(delivery);
730
+ if (settlement === "settled") {
731
+ this.settledItemIds.add(delivery.itemId);
732
+ this.pendingDeliveries.delete(delivery.itemId);
733
+ } else if (settlement === "retry") this.pendingDeliveries.set(delivery.itemId, delivery);
734
+ }
735
+ };
736
+ //#endregion
621
737
  //#region extensions/codex/src/app-server/event-projector-media.ts
622
738
  const GENERATED_IMAGE_MEDIA_SUBDIR = "tool-image-generation";
623
739
  var CodexGeneratedMediaProjection = class {
@@ -911,45 +1027,55 @@ function reasoningMethodOrder(method) {
911
1027
  const attemptTerminal = agentHarnessAttemptTerminal;
912
1028
  //#endregion
913
1029
  //#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
1030
  function buildCodexMessagesSnapshot(params) {
937
1031
  const messages = promptSnapshot(params.runParams, params.turnId, params.upstreamUserText);
938
1032
  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));
1033
+ const commentaryMessages = params.runParams.config?.ui?.prefs?.chatPersistCommentary === false ? [] : params.commentaryMessages.map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:commentary:${itemId}`));
1034
+ const asyncMessages = params.asyncMessages.map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:async:${itemId}`));
1035
+ const visibleWorkMessages = [
1036
+ ...commentaryMessages,
1037
+ ...asyncMessages,
1038
+ ...(params.assistantMessages ?? []).map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:assistant:${itemId}`)),
1039
+ ...params.toolMessages
1040
+ ].toSorted((left, right) => (asDateTimestampMs(left.timestamp) ?? 0) - (asDateTimestampMs(right.timestamp) ?? 0));
941
1041
  messages.push(...visibleWorkMessages);
942
1042
  if (params.lastAssistant) messages.push(attachCodexMirrorIdentity(params.lastAssistant, `${params.turnId}:assistant`));
943
- return applyStickyTurnTaint(messages).map((message) => projectAgentHarnessTranscriptMessageForDisplay({
1043
+ const taint = { tainted: false };
1044
+ return messages.map((message) => projectAgentHarnessTranscriptMessageForDisplay({
944
1045
  hidden: params.runParams.trigger === "memory",
945
- message
1046
+ message: applyCodexTranscriptTaint(message, taint)
946
1047
  }));
947
1048
  }
1049
+ function buildCodexSteeringMessagesSnapshot(params) {
1050
+ const asyncMessages = params.assistantProjection.collectAsyncMessages().filter(({ itemId }) => params.completedItemIds.has(itemId));
1051
+ const commentaryMessages = params.assistantProjection.collectCommentaryMessages().filter(({ itemId }) => params.completedItemIds.has(itemId));
1052
+ const assistantMessages = params.assistantProjection.collectCompletedAssistantMessages(params.completedItemIds, {
1053
+ tokenUsage: void 0,
1054
+ aborted: false,
1055
+ promptError: void 0
1056
+ });
1057
+ return {
1058
+ messages: buildCodexMessagesSnapshot({
1059
+ runParams: params.runParams,
1060
+ turnId: params.turnId,
1061
+ upstreamUserText: params.upstreamUserText,
1062
+ reasoningText: void 0,
1063
+ asyncMessages,
1064
+ commentaryMessages,
1065
+ assistantMessages,
1066
+ toolMessages: params.toolMessages,
1067
+ lastAssistant: void 0,
1068
+ createAssistantMirrorMessage: (title, text) => params.assistantProjection.createAssistantMirrorMessage(title, text)
1069
+ }).filter((message) => message.role !== "user"),
1070
+ assistantBoundaryItemId: assistantMessages.at(-1)?.itemId
1071
+ };
1072
+ }
948
1073
  //#endregion
949
1074
  //#region extensions/codex/src/app-server/event-projector-result.ts
950
1075
  function buildCodexAttemptResult(input) {
951
1076
  input.nativeToolLifecycleProjection.finalizeActive();
952
1077
  const assistantTexts = input.assistantProjection.collectAssistantTexts();
1078
+ const asyncMessages = input.assistantProjection.collectAsyncMessages();
953
1079
  const commentaryMessages = input.assistantProjection.collectCommentaryMessages();
954
1080
  const reasoningText = input.reasoningProjection.reasoningText();
955
1081
  const planText = input.reasoningProjection.planText();
@@ -984,7 +1110,7 @@ function buildCodexAttemptResult(input) {
984
1110
  turnId: input.turnId,
985
1111
  upstreamUserText: input.upstreamUserText,
986
1112
  reasoningText,
987
- planText,
1113
+ asyncMessages,
988
1114
  commentaryMessages,
989
1115
  toolMessages: input.toolTranscriptProjection.transcriptMessages,
990
1116
  lastAssistant,
@@ -1050,6 +1176,78 @@ function buildCodexAttemptResult(input) {
1050
1176
  };
1051
1177
  }
1052
1178
  //#endregion
1179
+ //#region extensions/codex/src/app-server/transcript-checkpoint.ts
1180
+ /** Commits completed work in receipt order without waiting for the enclosing turn. */
1181
+ var CodexTranscriptCheckpoint = class {
1182
+ constructor(params, threadId, turnId) {
1183
+ this.params = params;
1184
+ this.threadId = threadId;
1185
+ this.turnId = turnId;
1186
+ this.pending = [];
1187
+ this.commentaryItemIds = /* @__PURE__ */ new Set();
1188
+ this.lastTimestamp = 0;
1189
+ this.writing = Promise.resolve();
1190
+ this.tainted = false;
1191
+ this.closed = false;
1192
+ this.nextTimestamp = () => {
1193
+ this.lastTimestamp = Math.max(Date.now(), this.lastTimestamp + 1);
1194
+ return this.lastTimestamp;
1195
+ };
1196
+ this.enqueueCommentary = (itemId, entry) => {
1197
+ if (this.params.config?.ui?.prefs?.chatPersistCommentary === false || this.commentaryItemIds.has(itemId)) return;
1198
+ this.commentaryItemIds.add(itemId);
1199
+ this.enqueue({
1200
+ ...entry,
1201
+ read: () => {
1202
+ const message = entry.read();
1203
+ return message ? attachCodexMirrorIdentity(message, `${this.turnId}:commentary:${itemId}`) : void 0;
1204
+ }
1205
+ });
1206
+ };
1207
+ this.enqueue = (entry) => {
1208
+ if (this.params.sessionTarget && !this.closed) this.pending.push(entry);
1209
+ };
1210
+ }
1211
+ flush(close = false) {
1212
+ if (this.closed) return this.writing;
1213
+ this.closed = close;
1214
+ this.writing = this.writing.then(async () => {
1215
+ const blocked = close ? -1 : this.pending.findIndex((entry) => entry.ready?.() === false);
1216
+ const count = blocked < 0 ? this.pending.length : blocked;
1217
+ if (count === 0) return;
1218
+ const taint = { tainted: this.tainted };
1219
+ const messages = this.pending.slice(0, count).flatMap((entry) => {
1220
+ const message = entry.read();
1221
+ return message ? [projectAgentHarnessTranscriptMessageForDisplay({
1222
+ hidden: this.params.trigger === "memory",
1223
+ message: applyCodexTranscriptTaint(message, taint)
1224
+ })] : [];
1225
+ });
1226
+ try {
1227
+ await codexTranscriptMirrorRuntime.mirror({
1228
+ ...this.params.sessionTarget,
1229
+ sessionId: this.params.sessionId,
1230
+ cwd: this.params.workspaceDir,
1231
+ messages,
1232
+ idempotencyScope: `codex-app-server:${this.threadId}`,
1233
+ runId: this.params.runId,
1234
+ runMirrorIdentityPrefix: `${this.turnId}:`,
1235
+ config: this.params.config
1236
+ });
1237
+ this.pending.splice(0, count);
1238
+ this.tainted = taint.tainted;
1239
+ } catch (error) {
1240
+ embeddedAgentLog.warn("failed to checkpoint codex app-server transcript", {
1241
+ runId: this.params.runId,
1242
+ sessionId: this.params.sessionId,
1243
+ error: formatErrorMessage(error)
1244
+ });
1245
+ }
1246
+ });
1247
+ return this.writing;
1248
+ }
1249
+ };
1250
+ //#endregion
1053
1251
  //#region extensions/codex/src/app-server/usage-limit-error.ts
1054
1252
  /**
1055
1253
  * Enriches Codex usage-limit failures with current rate-limit information and
@@ -1204,7 +1402,8 @@ var CodexAppServerEventProjector = class {
1204
1402
  this.aborted = false;
1205
1403
  this.responseCompletions = new CodexResponseCompletionProjection();
1206
1404
  this.completedCompactionCount = 0;
1207
- this.lastTranscriptTimestamp = 0;
1405
+ this.transcriptCheckpoint = new CodexTranscriptCheckpoint(params, threadId, turnId);
1406
+ this.asyncDeliveryProjection = new CodexAsyncDeliveryProjection(params, threadId, turnId, options);
1208
1407
  this.contextTokens = options.initialContextTokens;
1209
1408
  this.contextTokensSource = options.initialContextTokens === void 0 ? void 0 : "resolved";
1210
1409
  this.diagnostics = new CodexProjectionDiagnostics(threadId, turnId);
@@ -1216,35 +1415,37 @@ var CodexAppServerEventProjector = class {
1216
1415
  signal: options.runAbortSignal
1217
1416
  });
1218
1417
  this.toolProgressProjection = new CodexToolProgressProjection(params);
1219
- this.toolTranscriptProjection = new CodexToolTranscriptProjection(params, threadId, turnId, this.toolProgressProjection, () => this.nextTranscriptTimestamp(), {
1418
+ this.toolTranscriptProjection = new CodexToolTranscriptProjection(params, threadId, turnId, this.toolProgressProjection, this.transcriptCheckpoint.nextTimestamp, {
1220
1419
  nativePostToolUseRelayEnabled: options.nativePostToolUseRelayEnabled,
1221
1420
  prepareNativeMcpAppResultDetails: options.prepareNativeMcpAppResultDetails,
1222
- trajectoryRecorder: options.trajectoryRecorder
1421
+ trajectoryRecorder: options.trajectoryRecorder,
1422
+ checkpointMessage: this.transcriptCheckpoint.enqueue
1223
1423
  });
1224
1424
  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());
1425
+ this.assistantProjection = new CodexAssistantProjection(params, (event) => this.emitAgentEvent(event), (text) => this.toolProgressProjection.matchesEcho(text), this.transcriptCheckpoint.nextTimestamp, this.transcriptCheckpoint.enqueueCommentary);
1226
1426
  this.reasoningProjection = new CodexReasoningProjection(params, (event) => this.emitAgentEvent(event), options.onNativePlanUpdate);
1227
1427
  }
1228
- nextTranscriptTimestamp() {
1229
- this.lastTranscriptTimestamp = Math.max(Date.now(), this.lastTranscriptTimestamp + 1);
1230
- return this.lastTranscriptTimestamp;
1231
- }
1232
1428
  getCompletedTurnStatus() {
1233
1429
  return this.completedTurn?.status;
1234
1430
  }
1235
1431
  buildSteeringTranscriptPrefix() {
1236
- const commentaryMessages = this.assistantProjection.collectCommentaryMessages().filter(({ itemId }) => this.completedItemIds.has(itemId));
1237
- return buildCodexMessagesSnapshot({
1432
+ const snapshot = buildCodexSteeringMessagesSnapshot({
1238
1433
  runParams: this.params,
1239
1434
  turnId: this.turnId,
1240
1435
  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");
1436
+ completedItemIds: this.completedItemIds,
1437
+ assistantProjection: this.assistantProjection,
1438
+ toolMessages: this.toolTranscriptProjection.transcriptMessages
1439
+ });
1440
+ this.pendingSteeringAssistantBoundaryItemId = snapshot.assistantBoundaryItemId;
1441
+ return snapshot.messages;
1442
+ }
1443
+ markSteeringTranscriptPersisted() {
1444
+ const itemId = this.pendingSteeringAssistantBoundaryItemId;
1445
+ if (itemId) {
1446
+ this.assistantProjection.markAssistantBoundaryPersisted(itemId);
1447
+ this.pendingSteeringAssistantBoundaryItemId = void 0;
1448
+ }
1248
1449
  }
1249
1450
  hasCompletedTerminalAssistantText() {
1250
1451
  return this.assistantProjection.hasCompletedTerminalAssistantText(this.completedItemIds);
@@ -1284,8 +1485,9 @@ var CodexAppServerEventProjector = class {
1284
1485
  if (!params) return;
1285
1486
  if (notification.method === "hook/started" || notification.method === "hook/completed") {
1286
1487
  if (!this.isHookNotificationForCurrentThread(params)) return;
1287
- } else if (notification.method === "guardianWarning") {
1288
- if (readCodexNotificationThreadId(params) !== this.threadId) return;
1488
+ } else if (notification.method === "guardianWarning" || notification.method === "warning" || notification.method === "configWarning") {
1489
+ const warningThreadId = readCodexNotificationThreadId(params);
1490
+ if (notification.method === "guardianWarning" && warningThreadId !== this.threadId || warningThreadId && warningThreadId !== this.threadId) return;
1289
1491
  } else if (!isCodexNotificationForTurn(params, this.threadId, this.turnId)) return;
1290
1492
  if (notification.method !== "guardianWarning" && notification.method !== "item/autoApprovalReview/started" && notification.method !== "item/autoApprovalReview/completed") this.eventProjection.flushPendingGuardianWarning();
1291
1493
  this.nativeToolLifecycleProjector.handleNotification(notification);
@@ -1306,9 +1508,11 @@ var CodexAppServerEventProjector = class {
1306
1508
  break;
1307
1509
  case "item/started":
1308
1510
  await this.handleItemStarted(params);
1511
+ await this.transcriptCheckpoint.flush();
1309
1512
  break;
1310
1513
  case "item/completed":
1311
1514
  await this.handleItemCompleted(params);
1515
+ await this.transcriptCheckpoint.flush();
1312
1516
  break;
1313
1517
  case "item/commandExecution/outputDelta":
1314
1518
  this.toolProgressProjection.handleOutputDelta(params, "bash");
@@ -1317,9 +1521,16 @@ var CodexAppServerEventProjector = class {
1317
1521
  case "item/autoApprovalReview/completed":
1318
1522
  this.eventProjection.handleGuardianReview(notification.method, params);
1319
1523
  break;
1524
+ case "autoApprovalReview/strictReviewRequired":
1525
+ this.eventProjection.handleStrictReviewRequired(params);
1526
+ break;
1320
1527
  case "guardianWarning":
1321
1528
  this.eventProjection.handleGuardianWarning(params);
1322
1529
  break;
1530
+ case "warning":
1531
+ case "configWarning":
1532
+ this.eventProjection.handleWarning(params);
1533
+ break;
1323
1534
  case "hook/started":
1324
1535
  case "hook/completed":
1325
1536
  this.eventProjection.handleHook(notification.method, params);
@@ -1331,7 +1542,7 @@ var CodexAppServerEventProjector = class {
1331
1542
  this.contextTokensSource = this.params.authoredContextTokenCap === void 0 ? "runtime" : "runtime-configured";
1332
1543
  }
1333
1544
  this.emitAgentEvent({
1334
- stream: "codex_app_server.usage",
1545
+ stream: "usage",
1335
1546
  data
1336
1547
  });
1337
1548
  });
@@ -1344,6 +1555,10 @@ var CodexAppServerEventProjector = class {
1344
1555
  break;
1345
1556
  case "rawResponseItem/completed":
1346
1557
  await this.handleRawResponseItemCompleted(params);
1558
+ await this.transcriptCheckpoint.flush();
1559
+ break;
1560
+ case "model/rerouted":
1561
+ this.eventProjection.handleModelRerouted(params);
1347
1562
  break;
1348
1563
  case "error": {
1349
1564
  this.responseCompletions.clear();
@@ -1363,7 +1578,6 @@ var CodexAppServerEventProjector = class {
1363
1578
  case "item/fileChange/outputDelta":
1364
1579
  case "item/fileChange/patchUpdated":
1365
1580
  case "item/mcpToolCall/progress":
1366
- case "model/rerouted":
1367
1581
  case "model/verification":
1368
1582
  case "turn/moderationMetadata":
1369
1583
  case "model/safetyBuffering/updated": break;
@@ -1495,7 +1709,9 @@ var CodexAppServerEventProjector = class {
1495
1709
  this.activeItemIds.delete(itemId);
1496
1710
  this.completedItemIds.add(itemId);
1497
1711
  }
1498
- this.assistantProjection.recordItemCompleted(item, itemId, this.activeItemIds);
1712
+ if (!this.asyncDeliveryProjection.allows(item, true)) return;
1713
+ const asyncMessage = this.assistantProjection.recordItemCompleted(item, itemId, this.activeItemIds);
1714
+ if (asyncMessage) await this.asyncDeliveryProjection.deliver(asyncMessage);
1499
1715
  this.reasoningProjection.recordItem(item);
1500
1716
  await this.generatedMediaProjection.recordNative(item);
1501
1717
  if (item?.type === "contextCompaction" && itemId) {
@@ -1525,9 +1741,9 @@ var CodexAppServerEventProjector = class {
1525
1741
  threadId: this.threadId,
1526
1742
  turnId: this.turnId,
1527
1743
  itemId,
1528
- timestamp: this.nextTranscriptTimestamp()
1744
+ timestamp: this.transcriptCheckpoint.nextTimestamp()
1529
1745
  });
1530
- this.emitCompactionEnd(itemId, true);
1746
+ this.eventProjection.emitCompactionEnd(itemId, true);
1531
1747
  }
1532
1748
  this.toolProgressProjection.recordToolMeta(item);
1533
1749
  this.toolProgressProjection.rememberCommandAggregateOutputEcho(item);
@@ -1573,10 +1789,11 @@ var CodexAppServerEventProjector = class {
1573
1789
  for (const itemId of failedCompactionItemIds) {
1574
1790
  this.activeItemIds.delete(itemId);
1575
1791
  this.activeCompactionItemIds.delete(itemId);
1576
- this.emitCompactionEnd(itemId, false);
1792
+ this.eventProjection.emitCompactionEnd(itemId, false);
1577
1793
  }
1578
1794
  }
1579
1795
  const turnItems = turn.items ?? [];
1796
+ const unsettledAsyncDeliveries = this.asyncDeliveryProjection.pending();
1580
1797
  for (let index = turnItems.length - 1; index >= 0; index -= 1) {
1581
1798
  const item = turnItems[index];
1582
1799
  if (!item || !this.isCurrentTurnSnapshotItem(item)) continue;
@@ -1587,8 +1804,10 @@ var CodexAppServerEventProjector = class {
1587
1804
  }
1588
1805
  }
1589
1806
  for (const item of turnItems) {
1807
+ if (!this.asyncDeliveryProjection.allows(item)) continue;
1590
1808
  this.diagnostics.warnUnknownItemStatus(item);
1591
- this.assistantProjection.recordSnapshotItem(item);
1809
+ const asyncMessage = this.assistantProjection.recordSnapshotItem(item);
1810
+ if (asyncMessage) await this.asyncDeliveryProjection.deliver(asyncMessage);
1592
1811
  this.reasoningProjection.recordItem(item);
1593
1812
  await this.generatedMediaProjection.recordNative(item);
1594
1813
  this.toolProgressProjection.recordToolMeta(item);
@@ -1601,23 +1820,11 @@ var CodexAppServerEventProjector = class {
1601
1820
  this.toolProgressProjection.emitToolResultOutput(item);
1602
1821
  }
1603
1822
  this.toolProgressProjection.approvalTimeoutKinds.clear();
1823
+ for (const delivery of unsettledAsyncDeliveries) await this.asyncDeliveryProjection.deliver(delivery);
1604
1824
  this.assistantProjection.finalizeAnswerCandidate(turn);
1605
1825
  this.activeCompactionItemIds.clear();
1606
1826
  await this.reasoningProjection.maybeEndReasoning();
1607
1827
  }
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
1828
  async emitSnapshotOnlyNativeToolProgress(item) {
1622
1829
  if (!shouldSynthesizeToolProgressForItem(item) || !this.isCurrentTurnSnapshotItem(item) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
1623
1830
  if (!this.activeItemIds.has(item.id)) {
@@ -1675,24 +1882,7 @@ var CodexAppServerEventProjector = class {
1675
1882
  return usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : readCodexErrorNotificationMessage(params);
1676
1883
  }
1677
1884
  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
- }
1885
+ emitCodexAgentEvent(this.params, event);
1696
1886
  }
1697
1887
  isHookNotificationForCurrentThread(params) {
1698
1888
  const threadId = readStringField(params, "threadId");
@@ -1701,11 +1891,28 @@ var CodexAppServerEventProjector = class {
1701
1891
  }
1702
1892
  };
1703
1893
  //#endregion
1894
+ //#region extensions/codex/src/app-server/mcp-tool-metadata.ts
1895
+ /** Hosted app ownership is authoritative only on metadata supplied by Codex. */
1896
+ function readCodexMcpToolConnectorId(tool) {
1897
+ const metadata = asOptionalRecord(asOptionalRecord(tool)?.["_meta"]);
1898
+ return normalizeOptionalString(metadata?.connector_id) ?? normalizeOptionalString(metadata?.connectorId);
1899
+ }
1900
+ /** Preserve MCP App visibility so model-only tools cannot become widget authority. */
1901
+ function readCodexMcpToolUiVisibility(tool) {
1902
+ const visibility = asOptionalRecord(asOptionalRecord(asOptionalRecord(tool)?.["_meta"])?.ui)?.visibility;
1903
+ if (!Array.isArray(visibility)) return;
1904
+ return [...new Set(visibility.filter((value) => value === "app" || value === "model"))].toSorted();
1905
+ }
1906
+ //#endregion
1704
1907
  //#region extensions/codex/src/app-server/native-mcp-app.ts
1908
+ const CODEX_APPS_MCP_SERVER$1 = "codex_apps";
1705
1909
  function readMcpAppResourceUri(item) {
1706
1910
  const uri = normalizeOptionalString(asOptionalRecord(item.appContext)?.resourceUri) ?? normalizeOptionalString(item.mcpAppResourceUri);
1707
1911
  return uri?.startsWith("ui://") ? uri : void 0;
1708
1912
  }
1913
+ function readMcpAppConnectorId(item) {
1914
+ return normalizeOptionalString(asOptionalRecord(item.appContext)?.connectorId);
1915
+ }
1709
1916
  function readMcpToolResult(item) {
1710
1917
  const result = asOptionalRecord(item.result);
1711
1918
  if (!result || !Array.isArray(result.content)) return;
@@ -1743,13 +1950,16 @@ function createNativeMcpRuntime(params) {
1743
1950
  launchSummary: "Codex native MCP connection",
1744
1951
  toolCount: Object.keys(status.tools).length
1745
1952
  }])),
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
- })))
1953
+ tools: loaded.flatMap((status) => statusTools(status).map((tool) => {
1954
+ const uiVisibility = readCodexMcpToolUiVisibility(tool);
1955
+ return Object.assign({
1956
+ serverName: status.name,
1957
+ safeServerName: status.name,
1958
+ toolName: String(tool.name),
1959
+ inputSchema: asOptionalRecord(tool.inputSchema) ?? { type: "object" },
1960
+ fallbackDescription: normalizeOptionalString(tool.description) ?? String(tool.name)
1961
+ }, uiVisibility ? { uiVisibility } : {});
1962
+ }))
1753
1963
  };
1754
1964
  return catalog;
1755
1965
  };
@@ -1777,11 +1987,18 @@ function createNativeMcpRuntime(params) {
1777
1987
  const status = (await loadStatuses()).find((entry) => entry.name === serverName);
1778
1988
  return { tools: status ? statusTools(status) : [] };
1779
1989
  },
1780
- readResource: async (serverName, uri) => await params.client.request("mcpServer/resource/read", {
1781
- threadId: params.threadId,
1782
- server: serverName,
1783
- uri
1784
- }),
1990
+ readResource: async (serverName, uri) => {
1991
+ const isCodexAppsServer = serverName === CODEX_APPS_MCP_SERVER$1;
1992
+ const response = await params.client.request("mcpServer/resource/read", {
1993
+ threadId: params.threadId,
1994
+ ...isCodexAppsServer ? { originCallId: params.originCallId } : {},
1995
+ server: serverName,
1996
+ uri,
1997
+ ...params.connectorId ? { connectorId: params.connectorId } : {}
1998
+ });
1999
+ if (isCodexAppsServer && response.originCallId !== params.originCallId) throw new Error(`Codex MCP resource response originCallId mismatch: expected ${params.originCallId}, received ${response.originCallId}`);
2000
+ return response;
2001
+ },
1785
2002
  listResources: async (serverName) => {
1786
2003
  return { resources: (await loadStatuses()).find((entry) => entry.name === serverName)?.resources ?? [] };
1787
2004
  },
@@ -1794,15 +2011,25 @@ function createNativeMcpRuntime(params) {
1794
2011
  }
1795
2012
  function createCodexNativeMcpAppResultDetailsPreparer(params) {
1796
2013
  if (params.attempt.config?.mcp?.apps?.enabled !== true) return;
1797
- const runtime = createNativeMcpRuntime(params);
1798
2014
  return async (item) => {
1799
2015
  const serverName = normalizeOptionalString(item.server);
1800
2016
  const toolName = normalizeOptionalString(item.tool);
1801
2017
  const uiResourceUri = readMcpAppResourceUri(item);
2018
+ const connectorId = readMcpAppConnectorId(item);
1802
2019
  const toolResult = readMcpToolResult(item);
1803
2020
  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;
2021
+ if (serverName === CODEX_APPS_MCP_SERVER$1 && !connectorId) return;
2022
+ const runtime = createNativeMcpRuntime({
2023
+ ...params,
2024
+ originCallId: item.id,
2025
+ ...connectorId ? { connectorId } : {}
2026
+ });
2027
+ const tools = (await runtime.listTools?.(serverName))?.tools ?? [];
2028
+ const allowedAppToolNames = new Set(tools.filter((tool) => {
2029
+ const uiVisibility = readCodexMcpToolUiVisibility(tool);
2030
+ return (uiVisibility === void 0 || uiVisibility.includes("app")) && (serverName !== CODEX_APPS_MCP_SERVER$1 || readCodexMcpToolConnectorId(tool) === connectorId);
2031
+ }).map((tool) => tool.name));
2032
+ if (!allowedAppToolNames.has(toolName)) return;
1806
2033
  return await prepareHarnessNativeMcpAppPreview({
1807
2034
  runtime,
1808
2035
  serverName,
@@ -2208,6 +2435,16 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2208
2435
  }, resourceState.thread.threadId, activeTurnId, {
2209
2436
  initialContextTokens: connection.mutable.startupContextTokens,
2210
2437
  nativePostToolUseRelayEnabled: resourceState.nativeHookRelay?.allowedEvents.includes("post_tool_use") === true && resourceState.nativeHookRelay.shouldRelayEvent("post_tool_use"),
2438
+ asyncUserMessageAllowed: params.disableTools !== true && (params.toolsAllow === void 0 || toolBridge.availableTools.some((tool) => tool.name === "message")),
2439
+ onAsyncDelivery: async (delivery) => {
2440
+ return await codexTranscriptMirrorRuntime.deliverAsyncMessageBestEffort({
2441
+ params: dynamicToolParams,
2442
+ cwd: effectiveCwd,
2443
+ threadId: resourceState.thread.threadId,
2444
+ turnId: activeTurnId,
2445
+ ...delivery
2446
+ });
2447
+ },
2211
2448
  readRecentRateLimits: () => readRecentCodexRateLimits(resourceState.client),
2212
2449
  runAbortSignal: runAbortController.signal,
2213
2450
  remoteWorkspaceRoot: connection.appServer.remoteWorkspaceRoot,
@@ -2265,6 +2502,18 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2265
2502
  turnWatches.armAttemptIdleWatch();
2266
2503
  turnWatches.touchActivity("turn:start", { attemptProgress: true });
2267
2504
  for (const failure of pendingNativePreToolUseFailures.splice(0)) activeProjector.recordNativeToolPreToolUseFailure(failure);
2505
+ const notifyUserMessagePersisted = createCodexAppServerUserMessagePersistenceNotifier(params);
2506
+ const promptMirrorPromise = mirrorPromptAtTurnStartBestEffort({
2507
+ params,
2508
+ agentId: sessionAgentId,
2509
+ notifyUserMessagePersisted,
2510
+ sessionKey: sandboxSessionKey,
2511
+ cwd: effectiveCwd,
2512
+ threadId: resourceState.thread.threadId,
2513
+ turnId: activeTurnId,
2514
+ upstreamUserText: turnState.codexTurnPromptText
2515
+ });
2516
+ await promptMirrorPromise;
2268
2517
  if (resourceState.turnRoute) try {
2269
2518
  await resourceState.turnRoute.bindTurn(activeTurnId);
2270
2519
  } catch (error) {
@@ -2289,40 +2538,65 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2289
2538
  turnId: activeTurnId
2290
2539
  });
2291
2540
  }
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
2541
+ const assertSteeringActive = () => {
2542
+ params.hostCapabilities.assertActive();
2543
+ runAbortController.signal.throwIfAborted();
2544
+ if (state.completed || state.terminalTurnNotificationQueued || state.timedOut) throw new Error("codex app-server turn is no longer accepting steering");
2545
+ };
2546
+ const workspaceOnly = resolveAttemptFsWorkspaceOnly({
2547
+ config: params.config,
2548
+ sessionAgentId
2302
2549
  });
2550
+ const imageContext = {
2551
+ workspaceDir: connection.effectiveWorkspace,
2552
+ model: params.model,
2553
+ config: params.config,
2554
+ workspaceOnly,
2555
+ localRoots: workspaceOnly ? void 0 : getAgentScopedMediaLocalRoots(params.config ?? {}, sessionAgentId),
2556
+ sandbox: connection.sandbox?.enabled && connection.sandbox.fsBridge ? {
2557
+ root: connection.sandbox.workspaceDir,
2558
+ bridge: connection.sandbox.fsBridge
2559
+ } : void 0
2560
+ };
2303
2561
  const activeSteeringQueue = createCodexSteeringQueue({
2304
2562
  client: resourceState.client,
2305
2563
  threadId: resourceState.thread.threadId,
2306
2564
  turnId: activeTurnId,
2307
2565
  requestTimeoutMs: connection.appServer.requestTimeoutMs,
2308
2566
  signal: runAbortController.signal,
2567
+ assertActive: assertSteeringActive,
2568
+ prepareMessage: async (text, options) => {
2569
+ const result = await detectAndLoadAgentHarnessPromptImages({
2570
+ ...imageContext,
2571
+ prompt: text,
2572
+ existingImages: options.images,
2573
+ imageOrder: options.imageOrder,
2574
+ media: options.media,
2575
+ userTurnTranscriptRecorder: options.userTurnTranscriptRecorder
2576
+ });
2577
+ if (result.failedMediaCount) throw new Error(`failed to hydrate ${result.failedMediaCount} structured image attachment(s) for Codex steering`);
2578
+ return buildCodexUserInput(text, result.images);
2579
+ },
2309
2580
  beforeConfirmConsumed: async (items) => {
2310
2581
  const inboundItems = items.filter((item) => item.isInboundUserMessage === true);
2311
2582
  if (inboundItems.length === 0) return;
2312
2583
  await promptMirrorPromise;
2313
2584
  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
- });
2585
+ if (params.sessionTarget && messages.length > 0) {
2586
+ await codexTranscriptMirrorRuntime.mirror({
2587
+ agentId: sessionAgentId,
2588
+ sessionKey: contextSessionKey,
2589
+ sessionId: params.sessionId,
2590
+ storePath: params.sessionTarget.storePath,
2591
+ cwd: effectiveCwd,
2592
+ messages,
2593
+ idempotencyScope: `codex-app-server:${resourceState.thread.threadId}`,
2594
+ runId: params.runId,
2595
+ runMirrorIdentityPrefix: `${activeTurnId}:`,
2596
+ config: params.config
2597
+ });
2598
+ activeProjector.markSteeringTranscriptPersisted();
2599
+ }
2326
2600
  for (const item of inboundItems) {
2327
2601
  const recorder = item.userTurnTranscriptRecorder;
2328
2602
  if (!recorder) continue;
@@ -2333,7 +2607,8 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2333
2607
  });
2334
2608
  steeringQueueRef.current = activeSteeringQueue;
2335
2609
  const claimPendingUserInputAnswer = async (text, optionsLocal) => {
2336
- if (optionsLocal?.isInboundUserMessage !== true || optionsLocal.images?.length) return false;
2610
+ if (optionsLocal?.isInboundUserMessage !== true || hasPromptImageInput(optionsLocal)) return false;
2611
+ assertSteeringActive();
2337
2612
  return await claimPendingAgentQuestionAnswer({
2338
2613
  sessionKey: params.sessionKey ?? params.sessionId,
2339
2614
  text,
@@ -2347,14 +2622,17 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2347
2622
  resolvedBy
2348
2623
  });
2349
2624
  const queueMessage = async (text, optionsLocal) => {
2350
- const isInboundUserMessage = optionsLocal?.isInboundUserMessage === true;
2351
2625
  if (await claimPendingUserInputAnswer(text, optionsLocal)) {
2352
2626
  optionsLocal?.onQueueAccepted?.(true);
2353
2627
  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 });
2628
+ }
2629
+ if (optionsLocal?.isInboundUserMessage === true && hasPromptImageInput(optionsLocal)) {
2630
+ assertSteeringActive();
2631
+ try {
2632
+ await cancelPendingUserInput("image-reply");
2633
+ } catch (error) {
2634
+ embeddedAgentLog.warn("failed to cancel codex gateway question before image steering", { error });
2635
+ }
2358
2636
  }
2359
2637
  try {
2360
2638
  await activeSteeringQueue.queue(text, optionsLocal);
@@ -2369,6 +2647,7 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2369
2647
  const handle = {
2370
2648
  kind: "embedded",
2371
2649
  runId: params.runId,
2650
+ startedAtMs: params.startedAtMs,
2372
2651
  toolAuthorityFingerprint: params.toolAuthorityFingerprint,
2373
2652
  claimPendingUserInputAnswer,
2374
2653
  cancelPendingUserInput,
@@ -2383,7 +2662,7 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2383
2662
  isAbortable: () => !terminalState.terminalOutcomeFrozen || terminalState.sharedAbortAllowedAfterTerminalOutcome,
2384
2663
  isCompacting: () => projectorRef.current?.isCompacting() ?? false,
2385
2664
  supportsTranscriptCommitWait: true,
2386
- supportsQueueMessageImages: true,
2665
+ supportsQueueMessageImages: params.model.input.includes("image"),
2387
2666
  sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
2388
2667
  taskSuggestionDeliveryMode: params.taskSuggestionDeliveryMode,
2389
2668
  cancel: () => abortExplicitly("cancelled"),
@@ -2412,7 +2691,15 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2412
2691
  })().finally(completeTurn);
2413
2692
  return;
2414
2693
  }
2415
- interruptTurn(activeTurnId).finally(completeTurn);
2694
+ const interrupted = interruptTurn(activeTurnId);
2695
+ if (terminalState.explicitCancellationObserved) state.abortCleanup = interrupted.then(async (confirmed) => {
2696
+ if (!confirmed) throw new Error("Codex cancellation could not confirm the turn stopped; background terminals may still be running.");
2697
+ await terminateCodexBackgroundTerminals(resourceState.client, resourceState.thread.threadId);
2698
+ });
2699
+ (terminalState.explicitCancellationObserved ? state.abortCleanup : interrupted).then(completeTurn, (error) => {
2700
+ embeddedAgentLog.warn("codex app-server cancellation cleanup failed", { error });
2701
+ completeTurn();
2702
+ });
2416
2703
  };
2417
2704
  runAbortController.signal.addEventListener("abort", abortListener, { once: true });
2418
2705
  if (runAbortController.signal.aborted) abortListener();
@@ -2437,6 +2724,8 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2437
2724
  const { codexModelCallDiagnostics } = requestRuntime;
2438
2725
  const { activeTurnId, abortListener, handle, freezeRunTerminalOutcome } = activeTurn;
2439
2726
  prompt.context.attemptTools.scheduledAppAuthoritySourceRef.current = void 0;
2727
+ freezeRunTerminalOutcome();
2728
+ await state.abortCleanup.catch(() => void 0);
2440
2729
  try {
2441
2730
  steeringQueueRef.current?.cancel();
2442
2731
  if (params.isFinalFallbackAttempt !== false) await maybeEmitFastModeAutoResetBestEffort();
@@ -2460,15 +2749,11 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2460
2749
  const retainLiveIncognitoThread = terminalState.turnSucceeded && isIncognitoSessionKey(params.sessionKey);
2461
2750
  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
2751
  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);
2752
+ return await retainCodexAppServerBindingSubscription(resourceState.client, resourceState.thread.threadId, {
2753
+ release: resourceState.thread.liveThreadOwnership?.release,
2754
+ configFingerprint: resourceState.thread.liveThreadConfigFingerprint,
2755
+ serviceTier: connection.mutable.pluginAppServer.serviceTier
2756
+ });
2472
2757
  }) : false;
2473
2758
  const retainLiveThread = retainLiveIncognitoThread || retainedPersistentThread;
2474
2759
  const bindingReleased = isIncognitoSessionKey(params.sessionKey) && !retainLiveIncognitoThread ? await bindingStore.mutate(bindingIdentity, {
@@ -2486,7 +2771,13 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2486
2771
  } finally {
2487
2772
  await runCleanupStep("codex-user-input-cancel", () => userInputBridgeRef.current?.cancelPending());
2488
2773
  await runCleanupStep("codex-turn-watch-clear", () => turnWatches.clearAllTimers());
2774
+ await runCleanupStep("codex-dynamic-tool-cleanup", async () => {
2775
+ const cleanupReason = terminalState.turnSucceeded ? "completion" : state.timedOut ? "timeout" : runAbortController.signal.aborted ? "cancel" : "error";
2776
+ const cleanups = prompt.context.attemptTools.runCleanups.splice(0);
2777
+ await Promise.allSettled(cleanups.map(async (cleanup) => await cleanup(cleanupReason)));
2778
+ });
2489
2779
  await runCleanupStep("codex-route-release", releaseCurrentRoute);
2780
+ await runCleanupStep("codex-transcript-checkpoint", () => activeTurn.activeProjector.transcriptCheckpoint.flush(true));
2490
2781
  await runCleanupStep("codex-shared-client-release", releaseSharedClientLeaseAndRetireOneShotClient);
2491
2782
  const nativeHookRelay = resourceState.nativeHookRelay;
2492
2783
  resourceState.nativeHookRelay = void 0;
@@ -2505,12 +2796,12 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2505
2796
  runAbortController.signal.removeEventListener("abort", abortListener);
2506
2797
  });
2507
2798
  await runCleanupStep("codex-steering-cancel", () => steeringQueueRef.current?.cancel());
2508
- await runCleanupStep("codex-terminal-freeze", freezeRunTerminalOutcome);
2509
2799
  await runCleanupStep("codex-reply-backend-detach", () => params.replyOperation?.detachBackend(handle));
2510
2800
  await runCleanupStep("codex-active-run-clear", () => {
2511
2801
  clearActiveEmbeddedRun(params.sessionId, handle, params.sessionKey, params.sessionFile);
2512
2802
  });
2513
2803
  }
2804
+ await state.abortCleanup;
2514
2805
  }
2515
2806
  //#endregion
2516
2807
  //#region extensions/codex/src/app-server/workspace-dir-cache.ts
@@ -2579,105 +2870,6 @@ async function runCodexAgentEndHook(params, hookParams) {
2579
2870
  runAgentEndSideEffects(sideEffectParams);
2580
2871
  }
2581
2872
  //#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
2873
  //#region extensions/codex/src/app-server/startup-binding.ts
2682
2874
  const CODEX_APP_SERVER_NATIVE_THREAD_FALLBACK_MAX_TOKENS = 3e5;
2683
2875
  const CODEX_APP_SERVER_NATIVE_THREAD_DEFAULT_RESERVE_TOKENS = 2e4;
@@ -2994,7 +3186,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
2994
3186
  const pluginConfig = readCodexPluginConfig(options.pluginConfig);
2995
3187
  const requirementsToml = readCodexRequirementsToml({});
2996
3188
  const computerUseConfig = resolveCodexComputerUseConfig({ pluginConfig });
2997
- const { sessionAgentId } = resolveSessionAgentIds({
3189
+ const { sessionAgentId } = resolveSessionAgentIdsStrict({
2998
3190
  sessionKey: params.sessionKey,
2999
3191
  config: params.config,
3000
3192
  agentId: params.agentId
@@ -3150,6 +3342,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3150
3342
  const sessionPermissionCwd = resolveCodexSessionPermissionCwd({
3151
3343
  permissionMode: params.permissionMode,
3152
3344
  sessionRoot: params.sessionRoot,
3345
+ defaultRoot: effectiveWorkspace,
3153
3346
  requestedCwd,
3154
3347
  fallbackCwd: effectiveWorkspace
3155
3348
  });
@@ -3160,14 +3353,20 @@ async function prepareCodexAttemptConnection({ params, options }) {
3160
3353
  appServer,
3161
3354
  permissionMode: params.permissionMode,
3162
3355
  sessionRoot: params.sessionRoot,
3356
+ defaultRoot: effectiveWorkspace,
3163
3357
  pluginConfig,
3164
3358
  canUseAutoReview: canUseCodexModelBackedApprovalsReviewerForModel({
3165
3359
  modelProvider: selection.modelProvider,
3166
3360
  model: selection.model,
3167
3361
  config: params.config,
3168
- env: process.env,
3362
+ env: {
3363
+ ...process.env,
3364
+ ...appServer.start.env,
3365
+ ...shellEnvironment
3366
+ },
3169
3367
  agentDir,
3170
- homeScope: appServer.start.homeScope
3368
+ homeScope: appServer.start.homeScope,
3369
+ codexArgs: appServer.start.args
3171
3370
  }),
3172
3371
  requirementsToml,
3173
3372
  policyLocked: startupBinding?.connectionScope === "supervision",
@@ -3180,7 +3379,11 @@ async function prepareCodexAttemptConnection({ params, options }) {
3180
3379
  provider: selection.modelProvider,
3181
3380
  model: selection.model,
3182
3381
  config: params.config,
3183
- env: process.env,
3382
+ env: {
3383
+ ...process.env,
3384
+ ...session.start.env,
3385
+ ...shellEnvironment
3386
+ },
3184
3387
  agentDir
3185
3388
  });
3186
3389
  return {
@@ -3244,6 +3447,17 @@ async function prepareCodexAttemptConnection({ params, options }) {
3244
3447
  resolvedAppServer = resolveFinalAppServer(configuredAppServer, reviewerPolicyContext);
3245
3448
  appServer = resolvedAppServer.appServer;
3246
3449
  }
3450
+ const sessionPermissionPolicy = resolveCodexEffectiveSessionPermissionPolicy({
3451
+ appServer,
3452
+ permissionMode: params.permissionMode,
3453
+ sessionRoot: params.sessionRoot,
3454
+ defaultRoot: effectiveWorkspace
3455
+ });
3456
+ if (sessionPermissionPolicy) {
3457
+ params.permissionMode = sessionPermissionPolicy.mode;
3458
+ params.sessionRoot = sessionPermissionPolicy.root;
3459
+ (params.execOverrides ??= {}).mode = sessionPermissionPolicy.execMode;
3460
+ }
3247
3461
  const nativeHookRelayEvents = resolveCodexNativeHookRelayEvents({
3248
3462
  configuredEvents: options.nativeHookRelay?.events,
3249
3463
  appServer
@@ -3290,6 +3504,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3290
3504
  effectiveWorkspace,
3291
3505
  effectiveCwd,
3292
3506
  appServer,
3507
+ sessionPermissionPolicy,
3293
3508
  nativeHookRelayEvents,
3294
3509
  runAbortController,
3295
3510
  terminalState,
@@ -4439,7 +4654,11 @@ function createCodexTrajectoryRecorder(params) {
4439
4654
  if (!params.trajectory) return null;
4440
4655
  const trajectory = params.trajectory;
4441
4656
  return {
4442
- recordEvent: trajectory.recordEvent,
4657
+ recordEvent: (type, data) => {
4658
+ try {
4659
+ trajectory.recordEvent(type, data);
4660
+ } catch {}
4661
+ },
4443
4662
  flush: trajectory.flush
4444
4663
  };
4445
4664
  }
@@ -4501,7 +4720,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4501
4720
  const { hookContextWindowFields, hookRunner } = context;
4502
4721
  const { connection, preparedAuthBinding } = runtime;
4503
4722
  const { effectiveRuntimeProviderId, effectiveRuntimeModelId } = runtime;
4504
- const { params, terminalState, runAbortController, activeContextEngine, bindingStore, bindingIdentity, appServer, usesSupervisionConnection, sessionAgentId, contextSessionKey, effectiveCwd, attemptStartedAt, startupAuthProfileId } = connection;
4723
+ const { params, terminalState, runAbortController, activeContextEngine, bindingStore, bindingIdentity, appServer, usesSupervisionConnection, sessionAgentId, contextSessionKey, effectiveCwd, agentDir, attemptStartedAt, startupAuthProfileId } = connection;
4505
4724
  const { toolBridge, toolState } = attemptTools;
4506
4725
  const { state, completion, pendingOpenClawDynamicToolCompletionIds, activeTurnItemIds, activeCompletionBlockerItemIds, activeFinalizationHookRunIds, turnWatches } = turnRuntime;
4507
4726
  const { emitLifecycleTerminal, buildLifecycleTerminalMeta } = lifecycle;
@@ -4509,7 +4728,20 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4509
4728
  const { codexModelCallDiagnostics } = requestRuntime;
4510
4729
  const { activeTurnId, activeProjector, streamState, freezeRunTerminalOutcome, notifyUserMessagePersisted } = activeTurn;
4511
4730
  await completion;
4512
- await drainNotificationQueue();
4731
+ await state.abortCleanup;
4732
+ const drain = drainNotificationQueue();
4733
+ const abortGraceElapsed = createDeferred();
4734
+ let abortGraceTimer;
4735
+ const abortListener = addAbortListener(runAbortController.signal, () => {
4736
+ abortGraceTimer = setTimeout(() => abortGraceElapsed.resolve(), TURN_FINALIZE_DRAIN_ABORT_GRACE_MS);
4737
+ abortGraceTimer.unref?.();
4738
+ });
4739
+ try {
4740
+ await Promise.race([drain, abortGraceElapsed.promise]);
4741
+ } finally {
4742
+ abortListener[Symbol.dispose]();
4743
+ clearTimeout(abortGraceTimer);
4744
+ }
4513
4745
  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
4746
  const hasRecoverableCompletedAssistant = !turnWatches.isCompletionIdleWatchPinnedByTerminalError() && turnWatches.isAssistantCompletionIdleWatchArmed() && hasQuiescentCompletedAssistant;
4515
4747
  const recoveredTurnWatchTimeout = state.turnCompletionIdleTimedOut && !terminalState.explicitCancellationObserved && hasRecoverableCompletedAssistant && activeProjector.recoverCompletedTerminalAssistantAfterTurnWatchTimeout();
@@ -4680,20 +4912,11 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4680
4912
  modelIterations: result.modelIterations ?? 0,
4681
4913
  skillWorkshopAvailable: flattenCodexDynamicToolFunctions(attemptTools.toolBridge.availableSpecs).some((tool) => tool.name === "skill_workshop"),
4682
4914
  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
4915
  senderId: params.senderId ?? void 0,
4693
- senderName: params.senderName,
4694
- senderUsername: params.senderUsername,
4695
- senderE164: params.senderE164,
4696
- senderIsOwner: params.senderIsOwner
4916
+ foregroundPromptContext: buildEmbeddedForegroundPromptContext({
4917
+ ...params,
4918
+ agentId: sessionAgentId
4919
+ }, agentDir)
4697
4920
  },
4698
4921
  hookRunner
4699
4922
  });
@@ -5854,10 +6077,6 @@ function resolveScheduledCodexAppCreatorCaptureDecision(params) {
5854
6077
  ...unavailableReason ? { unavailableReason } : {}
5855
6078
  };
5856
6079
  }
5857
- function readConnectorId(tool) {
5858
- const meta = asOptionalRecord(asOptionalRecord(tool)?.["_meta"]);
5859
- return normalizeOptionalString(meta?.connector_id) ?? normalizeOptionalString(meta?.connectorId);
5860
- }
5861
6080
  function normalizeApprovalMode(value) {
5862
6081
  return value === "allow" || value === "deny" || value === "auto" || value === "ask" ? value : void 0;
5863
6082
  }
@@ -5924,7 +6143,7 @@ async function readCodexScheduledAppToolNamesByApp(params) {
5924
6143
  if (!isJsonObject(status) || !isJsonObject(status.tools)) throw new Error("Codex scheduled app inventory contained an invalid server status");
5925
6144
  if (status.name !== CODEX_APPS_MCP_SERVER) continue;
5926
6145
  for (const [toolName, tool] of Object.entries(status.tools)) {
5927
- const connectorId = readConnectorId(tool);
6146
+ const connectorId = readCodexMcpToolConnectorId(tool);
5928
6147
  if (connectorId) {
5929
6148
  const names = toolNamesByApp.get(connectorId) ?? /* @__PURE__ */ new Set();
5930
6149
  names.add(toolName);
@@ -6518,6 +6737,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6518
6737
  data: {
6519
6738
  phase: "start",
6520
6739
  name: call.tool,
6740
+ itemId: call.callId,
6521
6741
  toolCallId: call.callId,
6522
6742
  ...toolMeta ? { meta: toolMeta } : {},
6523
6743
  ...toolArgs ? { args: toolArgs } : {},
@@ -6540,7 +6760,8 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6540
6760
  })) terminalDiagnosticObserved = true;
6541
6761
  }, { include: DYNAMIC_TOOL_TERMINAL_DIAGNOSTIC_TYPES });
6542
6762
  try {
6543
- const { execution } = openClawDynamicToolExecutions.claim(call, () => {
6763
+ const { execution } = openClawDynamicToolExecutions.claim(call, async () => {
6764
+ await projector?.transcriptCheckpoint.flush();
6544
6765
  emitDynamicToolStartedDiagnostic({
6545
6766
  call,
6546
6767
  agentId: sessionAgentId,
@@ -6548,7 +6769,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6548
6769
  sessionId: params.sessionId,
6549
6770
  sessionKey: params.sessionKey
6550
6771
  });
6551
- return handleDynamicToolCallWithTimeout({
6772
+ const response = await handleDynamicToolCallWithTimeout({
6552
6773
  call,
6553
6774
  toolBridge,
6554
6775
  signal,
@@ -6570,6 +6791,9 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6570
6791
  });
6571
6792
  }
6572
6793
  });
6794
+ recordCodexDynamicToolResult(projector, call, response, toCodexDynamicToolProtocolResponse(response));
6795
+ await projector?.transcriptCheckpoint.flush();
6796
+ return response;
6573
6797
  });
6574
6798
  const response = await execution;
6575
6799
  const protocolResponse = toCodexDynamicToolProtocolResponse(response);
@@ -6593,7 +6817,6 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6593
6817
  success: protocolResponse.success,
6594
6818
  contentItems: protocolResponse.contentItems
6595
6819
  });
6596
- recordCodexDynamicToolResult(projector, call, response, protocolResponse);
6597
6820
  if (protocolResponse.success && call.tool === "progress_card") {
6598
6821
  const progressCardInput = response.executedArguments ?? call.arguments;
6599
6822
  await projector?.recordDynamicProgressCardUpdate(progressCardInput);
@@ -6606,6 +6829,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6606
6829
  data: {
6607
6830
  phase: "result",
6608
6831
  name: call.tool,
6832
+ itemId: call.callId,
6609
6833
  toolCallId: call.callId,
6610
6834
  ...toolMeta ? { meta: toolMeta } : {},
6611
6835
  ...commandBearing ? { commandBearing: true } : {},
@@ -6681,7 +6905,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6681
6905
  //#region extensions/codex/src/app-server/attempt-runtime-artifact.ts
6682
6906
  async function verifyStartupArtifact(params) {
6683
6907
  if (!params.request) return;
6684
- const { readCodexAppServerClientRuntimeArtifact, validateCodexAppServerRuntimeArtifact } = await import("./runtime-artifact-BvyT1uyd.js");
6908
+ const { readCodexAppServerClientRuntimeArtifact, validateCodexAppServerRuntimeArtifact } = await import("./runtime-artifact-DeBvleJw.js");
6685
6909
  const runtimeArtifact = readCodexAppServerClientRuntimeArtifact(params.client);
6686
6910
  const expected = params.request.expected;
6687
6911
  const matchesExpected = !expected || runtimeArtifact?.id === expected.id && runtimeArtifact.fingerprint === expected.fingerprint;
@@ -6711,19 +6935,17 @@ function startCodexComputerUseHealthMonitor(params) {
6711
6935
  }
6712
6936
  const fingerprint = buildComputerUseHealthMonitorFingerprint(params.config);
6713
6937
  const intervalMs = params.config.healthCheckIntervalMinutes * 6e4;
6714
- if (existing?.fingerprint === fingerprint && existing.repairComputerUseMcpChildren === params.repairComputerUseMcpChildren) return {
6938
+ if (existing?.fingerprint === fingerprint) return {
6715
6939
  started: false,
6716
6940
  intervalMs,
6717
6941
  reason: "already_started"
6718
6942
  };
6719
6943
  if (existing) clearComputerUseHealthMonitor(params.client, existing);
6720
- const repairComputerUseMcpChildren = params.repairComputerUseMcpChildren ?? (() => killStaleComputerUseMcpChildren({ ancestorPid: params.client.getTransportPid() }));
6721
6944
  const monitor = {
6722
6945
  fingerprint,
6723
6946
  intervalMs,
6724
- repairComputerUseMcpChildren: params.repairComputerUseMcpChildren,
6725
6947
  timer: setInterval(() => {
6726
- runCodexComputerUseHealthProbe(params.client, params.config, monitor, { repairComputerUseMcpChildren });
6948
+ runCodexComputerUseHealthProbe(params.client, params.config, monitor);
6727
6949
  }, intervalMs),
6728
6950
  disposeCloseHandler: () => void 0,
6729
6951
  running: false
@@ -6748,13 +6970,12 @@ function buildComputerUseHealthMonitorFingerprint(config) {
6748
6970
  toolCallTimeoutMs: config.toolCallTimeoutMs
6749
6971
  });
6750
6972
  }
6751
- async function runCodexComputerUseHealthProbe(client, config, monitor, options) {
6973
+ async function runCodexComputerUseHealthProbe(client, config, monitor) {
6752
6974
  if (monitor.running) return;
6753
6975
  monitor.running = true;
6754
6976
  try {
6755
6977
  const { liveTest, repair } = await runCodexComputerUseLiveTest({
6756
6978
  config,
6757
- repairComputerUseMcpChildren: options.repairComputerUseMcpChildren,
6758
6979
  request: async (method, requestParams, requestOptions) => await client.request(method, requestParams, { timeoutMs: requestOptions?.timeoutMs ?? config.liveTestTimeoutMs })
6759
6980
  });
6760
6981
  if (!liveTest.ok) {
@@ -6767,10 +6988,7 @@ async function runCodexComputerUseHealthProbe(client, config, monitor, options)
6767
6988
  });
6768
6989
  return;
6769
6990
  }
6770
- if (repair?.killedPids.length) embeddedAgentLog.info("codex computer-use periodic health repaired stale children", {
6771
- mcpServerName: config.mcpServerName,
6772
- killedPids: repair.killedPids
6773
- });
6991
+ if (repair?.attempted && repair.warnings.length === 0) embeddedAgentLog.info("codex computer-use periodic health reloaded MCP servers", { mcpServerName: config.mcpServerName });
6774
6992
  } catch (error) {
6775
6993
  embeddedAgentLog.warn("codex computer-use periodic health probe crashed", {
6776
6994
  mcpServerName: config.mcpServerName,
@@ -6865,6 +7083,7 @@ function createCodexPluginThreadConfigStartupProvider(params) {
6865
7083
  build: async (buildOptions) => {
6866
7084
  const config = await buildCodexPluginThreadConfigWithinDeadline({
6867
7085
  ...buildParams,
7086
+ threadId: buildOptions?.threadId,
6868
7087
  appCache: appCache ?? defaultCodexAppInventoryCache,
6869
7088
  metadataCache,
6870
7089
  failClosedOnTimeout: Boolean(params.scheduledRuntimeAuthority),
@@ -6897,7 +7116,7 @@ function isCodexPluginThreadConfigTimeoutError(error) {
6897
7116
  * Startup orchestration for Codex app-server attempts, including shared-client
6898
7117
  * leasing, plugin thread config, sandbox environment, and thread lifecycle binding.
6899
7118
  */
6900
- const CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS = 3;
7119
+ const CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS = 3;
6901
7120
  const CODEX_APP_SERVER_CONTEXT_RESTART_SELECTION_CHANGED = "CODEX_APP_SERVER_CONTEXT_RESTART_SELECTION_CHANGED";
6902
7121
  /** True when a pre-write context restart must replay on the newly selected owner. */
6903
7122
  function isCodexContextRestartSelectionChangedError(error) {
@@ -7009,7 +7228,7 @@ async function startCodexAttemptThread(params) {
7009
7228
  signal: startupAbandonController.signal
7010
7229
  });
7011
7230
  } catch (error) {
7012
- if (startupAbandonController.signal.aborted) throw error;
7231
+ if (startupAbandonController.signal.aborted || isCodexAppServerStartSelectionChangedError(error)) throw error;
7013
7232
  throw new AgentHarnessPreflightError(`Codex Computer Use readiness failed: ${formatErrorMessage(error)}`, {
7014
7233
  cause: error,
7015
7234
  scope: "harness"
@@ -7023,7 +7242,8 @@ async function startCodexAttemptThread(params) {
7023
7242
  accountId: params.startupAuthAccountCacheKey,
7024
7243
  envApiKeyFingerprint: params.startupEnvApiKeyCacheKey,
7025
7244
  appServerVersion: activeStartupClient.getServerVersion(),
7026
- runtimeIdentity: startupRuntimeIdentity
7245
+ runtimeIdentity: startupRuntimeIdentity,
7246
+ desktopGenerationFingerprint: readCodexAppServerClientDesktopGenerationFingerprint(activeStartupClient)
7027
7247
  });
7028
7248
  const appServerRuntimeFingerprint = buildCodexAppServerRuntimeFingerprint({
7029
7249
  appServer: params.appServer,
@@ -7127,6 +7347,7 @@ async function startCodexAttemptThread(params) {
7127
7347
  finalConfigPatch: params.finalConfigPatch,
7128
7348
  buildFinalConfigPatch: params.buildFinalConfigPatch,
7129
7349
  nativeHookRelayGeneration: params.nativeHookRelayGeneration,
7350
+ nativeHookRelayRequired: params.nativeHookRelayRequired,
7130
7351
  nativeCodeModeEnabled: params.nativeToolSurfaceEnabled,
7131
7352
  nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
7132
7353
  nativeCodeModeOnlyEnabled: params.appServer.codeModeOnly,
@@ -7221,30 +7442,31 @@ async function startCodexAttemptThread(params) {
7221
7442
  }
7222
7443
  }
7223
7444
  };
7224
- for (let attempt = 1; attempt <= CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS; attempt += 1) try {
7445
+ for (let attempt = 1; attempt <= CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS; attempt += 1) try {
7225
7446
  return await startupAttempt();
7226
7447
  } catch (error) {
7227
7448
  const selectionChanged = isCodexAppServerStartSelectionChangedError(error);
7228
7449
  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) {
7450
+ const refreshedSharedClient = selectionChanged ? retireSharedCodexAppServerClientIfCurrent(attemptedClient) : clearSharedCodexAppServerClientIfCurrent(attemptedClient);
7451
+ if (startupClientForAbandonedRequestCleanup === attemptedClient) startupClientForAbandonedRequestCleanup = void 0;
7452
+ if (attempt >= CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS) {
7233
7453
  embeddedAgentLog.warn(selectionChanged ? "codex app-server executable selection kept changing during startup; retries exhausted" : "codex app-server connection closed during startup; retries exhausted", {
7234
7454
  attempt,
7235
- maxAttempts: CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS,
7455
+ maxAttempts: CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS,
7236
7456
  refreshedSharedClient,
7237
7457
  error: formatErrorMessage(error)
7238
7458
  });
7239
7459
  throw error;
7240
7460
  }
7461
+ const retryDelayMs = selectionChanged ? 0 : 1e3 * 2 ** (attempt - 1);
7241
7462
  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
7463
  attempt,
7243
7464
  nextAttempt: attempt + 1,
7244
- maxAttempts: CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS,
7465
+ maxAttempts: CODEX_APP_SERVER_STARTUP_MAX_ATTEMPTS,
7245
7466
  refreshedSharedClient,
7246
7467
  error: formatErrorMessage(error)
7247
7468
  });
7469
+ await sleepWithAbort(retryDelayMs, startupAbandonController.signal);
7248
7470
  }
7249
7471
  throw new Error("codex app-server startup retry loop exited unexpectedly");
7250
7472
  }
@@ -7257,7 +7479,7 @@ async function startCodexAttemptThread(params) {
7257
7479
  releaseSharedClientLease
7258
7480
  };
7259
7481
  } catch (error) {
7260
- if (params.signal.aborted || shouldClearSharedClientAfterStartupAbandon(error)) {
7482
+ if (params.signal.aborted || isCodexAppServerStartupError(error)) {
7261
7483
  releaseSharedClientLease?.();
7262
7484
  releaseSharedClientLease = void 0;
7263
7485
  await closeCodexStartupClientBestEffort(startupClientForAbandonedRequestCleanup);
@@ -7276,13 +7498,11 @@ async function startCodexAttemptThread(params) {
7276
7498
  params.signal.removeEventListener("abort", abandonStartupAcquire);
7277
7499
  }
7278
7500
  }
7279
- function shouldClearSharedClientAfterStartupAbandon(error) {
7280
- return isCodexAppServerStartupError(error);
7281
- }
7282
7501
  function shouldClearSharedClientAfterStartupRace(error) {
7283
- return shouldClearSharedClientAfterStartupAbandon(error) || isCodexAppServerRequestTimeoutError(error);
7502
+ return isCodexAppServerStartupError(error) || isCodexAppServerRequestTimeoutError(error);
7284
7503
  }
7285
7504
  function shouldClearSharedClientAfterStartupFailure(params) {
7505
+ if (isCodexAppServerOverloadError(params.error)) return false;
7286
7506
  return isCodexAppServerBrokenPipeError(params.error) || !params.spawnedBy;
7287
7507
  }
7288
7508
  //#endregion
@@ -7296,6 +7516,7 @@ async function startCodexAttemptRuntime(resources) {
7296
7516
  const developerInstructions = joinPresentSections(turnState.promptBuild.developerInstructions, attemptTools.scheduledConfiguredMcp?.diagnosticNotice);
7297
7517
  const { params, attemptClientFactory, bindingStore, appServer, pluginConfig, computerUseConfig, startupClientAuthProfileId, runtimeArtifactRequest, startupPreparedAuth, agentDir, sessionAgentId, effectiveWorkspace, effectiveCwd, sandbox, runAbortController, usesSupervisionConnection, resolveReviewerPolicyContext, resolveRuntimeOptionsForCurrentBinding, startupAuthProfileId, startupAuthRequirement, abortFromUpstream } = connection;
7298
7518
  let pluginAppServer = withCodexAppServerFastModeServiceTier(appServer, runtimeParams);
7519
+ const loopDetectionEnabled = (sessionAgentId && params.config ? resolveAgentConfig$1(params.config, sessionAgentId)?.tools?.loopDetection?.enabled : void 0) ?? params.config?.tools?.loopDetection?.enabled ?? false;
7299
7520
  try {
7300
7521
  emitCodexAppServerEvent(params, {
7301
7522
  stream: "codex_app_server.lifecycle",
@@ -7330,6 +7551,7 @@ async function startCodexAttemptRuntime(resources) {
7330
7551
  developerInstructions,
7331
7552
  agentWorkspaceDeveloperInstructions: context.agentWorkspaceDeveloperInstructions,
7332
7553
  buildFinalConfigPatch: buildNativeHookRelayFinalConfigPatch,
7554
+ nativeHookRelayRequired: connection.options.nativeHookRelay?.enabled !== false && params.pluginHarnessToolPolicyRestricted !== true && connection.nativeHookRelayEvents.includes("pre_tool_use") && (hasBeforeToolCallPolicy() || appServer.loopDetectionPreToolUseRelay && Boolean(connection.sandboxSessionKey) && loopDetectionEnabled),
7333
7555
  bundleMcpThreadConfig,
7334
7556
  configuredMcpOwnershipVersion: attemptTools.configuredMcpOwnershipVersion,
7335
7557
  nativeToolSurfaceEnabled,
@@ -7436,7 +7658,7 @@ function isAuthorityResolutionOperationAbort(error, signal) {
7436
7658
  }
7437
7659
  async function prepareCodexAttemptTools(runtime) {
7438
7660
  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;
7661
+ const { params, preDynamicStartupStages, mutable, startupAuthProfileId, resolvedWorkspace, effectiveWorkspace, effectiveCwd, sandboxSessionKey, sandbox, sessionPermissionPolicy, runAbortController, sessionAgentId, pluginConfig, profilerEnabled, agentDir } = connection;
7440
7662
  const preDynamicSummary = preDynamicStartupStages.snapshot();
7441
7663
  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
7664
  runId: params.runId,
@@ -7479,6 +7701,7 @@ async function prepareCodexAttemptTools(runtime) {
7479
7701
  ...onCodexToolOutcome ? { onToolOutcome: onCodexToolOutcome } : {}
7480
7702
  };
7481
7703
  const computerContextEpoch = { value: 0 };
7704
+ const runCleanups = [];
7482
7705
  const cronCreatorToolAllowlist = [];
7483
7706
  const cronCreatorToolAllowlistCaptureRef = {};
7484
7707
  const scheduledAppAuthoritySourceRef = {};
@@ -7509,6 +7732,7 @@ async function prepareCodexAttemptTools(runtime) {
7509
7732
  effectiveCwd,
7510
7733
  sandboxSessionKey,
7511
7734
  sandbox,
7735
+ sessionPermissionPolicy,
7512
7736
  nativeToolSurfaceEnabled,
7513
7737
  nativeProviderWebSearchSupport,
7514
7738
  runAbortController,
@@ -7539,6 +7763,7 @@ async function prepareCodexAttemptTools(runtime) {
7539
7763
  };
7540
7764
  const tools = await buildDynamicTools({
7541
7765
  ...commonToolParams,
7766
+ registerRunCleanup: (cleanup) => runCleanups.push(cleanup),
7542
7767
  cronCreatorToolAllowlistRef: cronCreatorToolAllowlist,
7543
7768
  cronCreatorToolAllowlistCaptureRef,
7544
7769
  onPersistentWebSearchPolicyResolved: (allowed) => {
@@ -7759,6 +7984,7 @@ async function prepareCodexAttemptTools(runtime) {
7759
7984
  dynamicToolParams,
7760
7985
  compactionPlanState,
7761
7986
  computerContextEpoch,
7987
+ runCleanups,
7762
7988
  toolBridge,
7763
7989
  toolState,
7764
7990
  toolOutcomeOrdinals,
@@ -8240,8 +8466,10 @@ function createCodexAttemptTurnWatchController(params) {
8240
8466
  function scheduleAttemptIdleWatch() {
8241
8467
  scheduleWatch("attempt", fireAttemptIdleTimeout, attemptLastProgressAt, attemptIdleTimeoutOverrideMs ?? turnAttemptIdleTimeoutMs, attemptIdleWatchArmed && !shouldPauseAttemptIdleWatch());
8242
8468
  }
8469
+ const terminalWorkIsActive = () => params.getActiveAppServerTurnRequests() > 0 || params.isTerminalTurnNotificationQueued() && params.getActiveFinalizationHookCount() > 0;
8470
+ const terminalIdleTimeoutMs = () => params.isTerminalTurnNotificationQueued() && !terminalWorkIsActive() ? Math.min(TURN_TERMINAL_SETTLEMENT_IDLE_TIMEOUT_MS, turnTerminalIdleTimeoutMs) : turnTerminalIdleTimeoutMs;
8243
8471
  function scheduleTerminalIdleWatch() {
8244
- scheduleWatch("terminal", fireTerminalIdleTimeout, completionLastActivityAt, turnTerminalIdleTimeoutMs, terminalIdleWatchArmed && params.getActiveAppServerTurnRequests() === 0);
8472
+ scheduleWatch("terminal", fireTerminalIdleTimeout, completionLastActivityAt, terminalIdleTimeoutMs(), terminalIdleWatchArmed && (params.isTerminalTurnNotificationQueued() || params.getActiveAppServerTurnRequests() === 0));
8245
8473
  }
8246
8474
  function scheduleProgressWatches() {
8247
8475
  scheduleAttemptIdleWatch();
@@ -8362,18 +8590,26 @@ function createCodexAttemptTurnWatchController(params) {
8362
8590
  });
8363
8591
  }
8364
8592
  function fireTerminalIdleTimeout() {
8365
- if (params.isCompleted() || params.isTerminalTurnNotificationQueued() || params.signal.aborted || !terminalIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0) return;
8593
+ if (params.isCompleted() || params.signal.aborted || !terminalIdleWatchArmed) return;
8594
+ if (!params.isTerminalTurnNotificationQueued() && params.getActiveAppServerTurnRequests() > 0) {
8595
+ scheduleTerminalIdleWatch();
8596
+ return;
8597
+ }
8598
+ const timeoutMs = terminalIdleTimeoutMs();
8366
8599
  const idleMs = Math.max(0, Date.now() - completionLastActivityAt);
8367
- if (idleMs < turnTerminalIdleTimeoutMs) {
8600
+ if (idleMs < timeoutMs) {
8368
8601
  scheduleTerminalIdleWatch();
8369
8602
  return;
8370
8603
  }
8371
8604
  reportTimeout({
8372
8605
  kind: "terminal",
8373
8606
  idleMs,
8374
- timeoutMs: turnTerminalIdleTimeoutMs,
8607
+ timeoutMs,
8375
8608
  lastActivityReason: completionLastActivityReason,
8376
- details: completionLastActivityDetails
8609
+ details: {
8610
+ ...completionLastActivityDetails,
8611
+ terminalTurnNotificationQueued: params.isTerminalTurnNotificationQueued()
8612
+ }
8377
8613
  });
8378
8614
  }
8379
8615
  return {
@@ -8430,6 +8666,7 @@ function createCodexAttemptTurnWatchController(params) {
8430
8666
  completionLastActivityReason = `notification:${method}`;
8431
8667
  if (options?.details !== void 0) completionLastActivityDetails = options.details;
8432
8668
  if (options?.attemptProgress) recordAttemptProgress(completionLastActivityReason, options);
8669
+ if (params.isTerminalTurnNotificationQueued()) scheduleTerminalIdleWatch();
8433
8670
  },
8434
8671
  extendAttemptIdleWatch: (timeoutMs) => {
8435
8672
  attemptIdleTimeoutOverrideMs = resolveWatchTimeoutMs(timeoutMs);
@@ -8452,6 +8689,7 @@ function createCodexAttemptTurnState(resources) {
8452
8689
  latestStartupErrorNotification: void 0,
8453
8690
  rateLimitsRevisionBeforeLastTurnStart: void 0,
8454
8691
  completed: false,
8692
+ abortCleanup: Promise.resolve(),
8455
8693
  localCompletionRequested: false,
8456
8694
  terminalTurnNotificationQueued: false,
8457
8695
  sawCodexInterruptMarker: false,