@openclaw/codex 2026.9.1-beta.1 → 2026.9.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 (73) hide show
  1. package/dist/api.js +5 -6
  2. package/dist/{attempt-client-cleanup-bfsmcQ6m.js → attempt-client-cleanup-DYHcRQAa.js} +31 -45
  3. package/dist/attempt-context-DmQUNWwT.js +634 -0
  4. package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
  5. package/dist/{binding-connection-BMvPX73o.js → binding-connection-E6uDsPlx.js} +2 -2
  6. package/dist/{bounded-turn-B7iBUcuP.js → bounded-turn-wzJrwKKe.js} +15 -20
  7. package/dist/{capabilities-BRMh0aIq.js → capabilities-dIjNVCYH.js} +1 -1
  8. package/dist/cli-metadata.js +1 -1
  9. package/dist/{command-formatters-DEsIufkt.js → command-formatters-CLHhEB1Q.js} +5 -4
  10. package/dist/{command-handlers-D_-1_qRp.js → command-handlers-CTGb2csu.js} +124 -101
  11. package/dist/{compact-BglNFZrQ.js → compact-_t0HQrUS.js} +38 -29
  12. package/dist/{config-giW8X0QA.js → config-BhmfC1s6.js} +294 -282
  13. package/dist/{context-compaction-activity-BOeJf7yo.js → context-compaction-activity-CMla5FPJ.js} +4 -1
  14. package/dist/{conversation-binding-data-CW2GuupV.js → conversation-binding-data-BiodpIzq.js} +2 -1
  15. package/dist/doctor-contract-api.js +143 -12
  16. package/dist/{dynamic-tools-Dnlfa--w.js → dynamic-tools-BU9tmlYm.js} +599 -655
  17. package/dist/{effective-mcp-catalog-CTeGrnfO.js → effective-mcp-catalog-DzYBW4h1.js} +2 -2
  18. package/dist/{event-projector-assistant-message-C5-Ds8mA.js → event-projector-assistant-message-DHRFXKbS.js} +4 -4
  19. package/dist/{event-projector-usage-Bk1I90Bg.js → event-projector-usage-C5mcrGjp.js} +15 -148
  20. package/dist/{harness-Csz0F-DK.js → harness-DXVZ5II5.js} +42 -52
  21. package/dist/harness.js +1 -1
  22. package/dist/index.js +458 -404
  23. package/dist/{isolated-completion-Cv6FqygM.js → isolated-completion-kg9kv4iH.js} +4 -4
  24. package/dist/launch-args-RXQwn8zV.js +90 -0
  25. package/dist/{managed-binary-DFYK4DWq.js → managed-binary-4-Vjz5GH.js} +14 -9
  26. package/dist/media-understanding-provider.js +1 -1
  27. package/dist/model-catalog-CUM68Hkl.js +101 -0
  28. package/dist/{models-TS3HBU-q.js → models-sYnEc0gd.js} +25 -28
  29. package/dist/{native-subagent-monitor-4Mfq2HDs.js → native-subagent-monitor-CnH147jH.js} +55 -115
  30. package/dist/{node-exec-server.runtime-BpWqJO1V.js → node-exec-server.runtime-CstNHCmC.js} +9 -6
  31. package/dist/{plugin-approval-roundtrip-bUm777tR.js → plugin-approval-roundtrip-CNe-Xrjx.js} +30 -3
  32. package/dist/{plugin-marketplace-discovery-B9DvaAG-.js → plugin-marketplace-discovery-CfFlKqDl.js} +15 -14
  33. package/dist/{plugin-metadata-cache-CTCT3w5L.js → plugin-metadata-cache-BFrPhb2c.js} +6 -6
  34. package/dist/provider-capabilities-DW5p4k9w.js +101 -0
  35. package/dist/{rate-limits-C5_D8Gmb.js → rate-limits-CawQJfUS.js} +3 -2
  36. package/dist/{sandbox-guard-kuWTn9C5.js → request-DYAI5FQE.js} +195 -2
  37. package/dist/{run-attempt-DYc0QCBF.js → run-attempt-D1hJO4fX.js} +828 -2100
  38. package/dist/{runtime-artifact-BvyT1uyd.js → runtime-artifact-C3jVimFn.js} +21 -14
  39. package/dist/{session-binding-Bcynfd-_.js → session-binding-BHuTCRZQ.js} +198 -127
  40. package/dist/{session-catalog-DaLKVtpH.js → session-catalog-yyvTWFOw.js} +564 -408
  41. package/dist/{session-cli-DMc4oS2w.js → session-cli-CmFEXMAM.js} +1 -1
  42. package/dist/session-history-C5J1AKzG.js +87 -0
  43. package/dist/session-initialization-BeWzjGiR.js +74 -0
  44. package/dist/{session-permission-policy-BrNH1bjy.js → session-permission-policy-DeWVTCMj.js} +2 -2
  45. package/dist/session-retirement-BujDjXVF.js +97 -0
  46. package/dist/{session-upstream-marker-BIkusb95.js → session-upstream-marker-C351-5Qw.js} +19 -4
  47. package/dist/settled-turn-context-D3SNWtUn.js +268 -0
  48. package/dist/settled-turn-finalizer-BVJx4yoc.js +104 -0
  49. package/dist/{shared-client-DKz23hHY.js → shared-client-DxcfF9KX.js} +3480 -679
  50. package/dist/{side-question-DjA_jsNe.js → side-question-BTZAII4l.js} +108 -70
  51. package/dist/{thread-lifecycle-L7oEkTlq.js → thread-lifecycle-DAcEJ_Om.js} +2188 -1915
  52. package/dist/{thread-ownership-DZeuKH1j.js → thread-ownership-zGokd8i1.js} +25 -14
  53. package/dist/thread-policy-g4KbJPzp.js +54 -0
  54. package/dist/{plugin-app-cache-key-C1CAdfcP.js → thread-resume-C7pyVKeW.js} +59 -7
  55. package/dist/{transcript-mirror-DsCMWBca.js → transcript-mirror-DTUxFWa_.js} +79 -17
  56. package/dist/transport-stdio-CyPldczm.js +726 -0
  57. package/dist/{upstream-prompt-provenance-CFmsKwr9.js → upstream-prompt-provenance-LphB8slG.js} +8 -4
  58. package/dist/upstream-session-fork-CYlBIGVt.js +966 -0
  59. package/dist/{usage-ROIaW3wo.js → usage-Bj-rrzkR.js} +3 -3
  60. package/dist/{web-search-provider.runtime-ClaNPV7N.js → web-search-provider.runtime-D2FWSK4w.js} +3 -3
  61. package/openclaw.plugin.json +1 -30
  62. package/package.json +6 -6
  63. package/dist/command-rpc-CCeCkf_v.js +0 -90
  64. package/dist/incognito-session-ET_ufDH-.js +0 -2
  65. package/dist/model-catalog-i9rNBthX.js +0 -48
  66. package/dist/protocol-validators-VeJxxJ0U.js +0 -2421
  67. package/dist/request-DXXGHko0.js +0 -157
  68. package/dist/session-catalog-home-id-BNwAJ9D7.js +0 -18
  69. package/dist/session-retirement-CcNC3YSY.js +0 -36
  70. package/dist/settled-turn-finalizer-Dc3ApjU4.js +0 -328
  71. package/dist/thread-resume-BE9qNEW0.js +0 -30
  72. package/dist/transport-stdio-DhoGy33Z.js +0 -99
  73. package/dist/upstream-session-fork-DlaHxSLC.js +0 -326
@@ -1,49 +1,54 @@
1
- import { C as resolveCodexModelBackedReviewerPolicyContext, D as readCodexRequirementsToml, d as withMcpElicitationsApprovalPolicy, f as isCodexPairedNodeRemoteExecPlacementSandbox, g as resolveCodexPluginsPolicy, h as readCodexPluginConfig, j as resolveOpenClawExecPolicyForCodexAppServer, m as isCodexSandboxExecServerEnabled, o as resolveCodexComputerUseConfig, p as isCodexRemoteExecPlacementSandbox, r as resolveCodexAppServerHomeScope, u as shouldAutoApproveCodexAppServerApprovals, x as canUseCodexModelBackedApprovalsReviewerForModel } from "./config-giW8X0QA.js";
2
- import { A as resolveCodexTurnAssistantCompletionIdleTimeoutMs, E as isCodexAppServerStartupError, F as isCodexAppServerApprovalRequest, G as CodexAppServerRpcError, I as isCodexAppServerBrokenPipeError, K as createCodexElicitationResponse, L as isCodexAppServerConnectionClosedError, M as resolveCodexTurnTerminalIdleTimeoutMs, N as withCodexStartupTimeout, O as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, P as getCodexAppServerClientInstanceId, Q as protectCodexAppServerLiveThread, R as isCodexAppServerIndeterminateRequestCancellationError, T as CodexAppServerStartupError, U as resolveCodexAppServerClientInstanceId, V as isCodexAppServerRequestTimeoutError, Y as ensureCodexAppServerClientRuntime, _ as retireSharedCodexAppServerClientIfCurrent, _t as flattenCodexDynamicToolFunctions, a as createIsolatedCodexAppServerClient, at as readRecentCodexRateLimits, c as isCodexAppServerStartSelectionChangedError, ct as resolveCodexAppServerAuthAccountCacheKey, dt as resolveCodexAppServerFallbackApiKeyCacheKey, et as retainCodexAppServerLiveThread, ft as resolveCodexAppServerPreparedApiKeyCacheKey, g as retainSharedCodexAppServerClientIfCurrent, it as readCodexRateLimitsRevision, j as resolveCodexTurnCompletionIdleTimeoutMs, k as resolveCodexStartupTimeoutMs, lt as resolveCodexAppServerAuthProfileId, n as clearSharedCodexAppServerClientIfCurrent, nt as withAbortableTimeout, o as getLeasedSharedCodexAppServerClient, ot as rememberCodexRateLimitsRead, p as releaseLeasedSharedCodexAppServerClient, pt as resolveCodexAppServerPreparedAuthHandoff, r as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, s as getSharedCodexAppServerClient, u as readCodexAppServerClientDesktopGenerationFingerprint, ut as resolveCodexAppServerAuthProfileIdForAgent, vt as isJsonObject, w as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, xt as resolveCodexAppServerHomeDir, z as isCodexAppServerIndeterminateTransportError } from "./shared-client-DKz23hHY.js";
3
- import { c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, i as createCodexSessionGenerationSupersededError, l as resolveCodexRunSessionBindingAuthority, u as scopeCodexRunBindingStore } from "./session-binding-Bcynfd-_.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-bfsmcQ6m.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-Bk1I90Bg.js";
6
- import { l as readCodexTurn, o as assertCodexTurnStartResponse, s as readCodexDynamicToolCallParams, u as readCodexTurnCompletedNotification } from "./protocol-validators-VeJxxJ0U.js";
7
- import { n as buildCodexAppServerRuntimeFingerprint, o as defaultCodexAppInventoryCache, r as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-C1CAdfcP.js";
8
- import { C as toCodexDynamicToolProgressResponse, Dt as resolveRecoverableCodexPluginConfigKeys, F as resolveCodexWebSearchPlan, I as buildDeveloperInstructions, J as areCodexDynamicToolFingerprintsCompatible, S as shouldReleaseTurnAfterTerminalDynamicTool, T as resolveCodexToolAbortTerminalReason, V as resolveCodexAppServerThreadModelSelection, X as codexLegacyDynamicToolsFingerprint, Y as codexDynamicToolsFingerprint, Z as filterCodexDynamicTools, _ as isDynamicToolTerminalDiagnosticEvent, _t as buildCodexPluginThreadConfigTimeoutFallback, a as isCodexAppServerProfilerEnabled, at as buildContextEngineBinding, b as resolveTerminalDynamicToolBatchAction, bt as mergeCodexThreadConfigs, c as buildCodexNativeHookRelayConfig, ct as fitCodexProjectedContextForTurnStart, d as emitCodexNativePreToolUseFailureDiagnostic, dt as resolveCodexContextEngineProjectionMaxChars, et as isMessageOnlyCodexSourceReply, f as resolveCodexNativeHookRelayEvents, ft as resolveCodexContextEngineProjectionReserveTokens, g as hasPendingDynamicToolTerminalDiagnostic, gt as buildCodexPluginThreadConfigInputFingerprint, h as handleDynamicToolCallWithTimeout, ht as buildCodexPluginThreadConfig, i as startOrResumeThread, it as resolveCodexDynamicToolsLoadingForRuntime, l as buildCodexNativeHookRelayDisabledConfig, lt as neutralizeCodexExplicitMentionSigils, m as scheduleCodexNativeHookRelayUnregister, mt as buildCodexPluginAppsConfigPatchFromPolicyContext, n as buildTurnStartParams, ot as isContextEngineBindingCompatible, p as resolveCodexNativeHookRelayTtlMs, pt as resolveCodexContinuityProjectionMaxChars, r as buildCodexUserInput, s as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, st as buildCodexContinuityCalibration, t as buildTurnCollaborationMode, tt as isSystemAgentOnlyCodexDynamicToolAllowlist, u as createCodexNativeHookRelay, ut as projectContextEngineAssemblyForCodex, v as isMatchingDynamicToolTerminalDiagnostic, w as toCodexDynamicToolProtocolResponse, x as shouldBlockTerminalReleaseForNonTerminalDynamicToolResult, xt as shouldBuildCodexPluginThreadConfig, y as resolveDynamicToolCallTimeoutMs, yt as buildPluginAppPolicyContext } from "./thread-lifecycle-L7oEkTlq.js";
9
- import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
10
- import { i as readUpstreamUserText, n as attachUpstreamUserText, r as readMirrorIdentity, t as attachCodexMirrorIdentity } from "./upstream-prompt-provenance-CFmsKwr9.js";
11
- import { a as buildCodexUserPromptMessage, c as readCodexMirrorSourceFingerprint, i as mirrorPromptAtTurnStartBestEffort, l as serializeCodexMirrorSourceEvidence, n as createCodexAppServerUserMessagePersistenceNotifier, o as promptSnapshot, t as codexTranscriptMirrorRuntime } from "./transcript-mirror-DsCMWBca.js";
12
- import { t as CODEX_CONTROL_METHODS } from "./capabilities-BRMh0aIq.js";
13
- import { a as shouldRefreshCodexRateLimitsForUsageLimitMessage, i as resolveCodexUsageLimitResetAtMs, n as formatCodexUsageLimitErrorMessage } from "./rate-limits-C5_D8Gmb.js";
14
- import { r as formatCodexDisplayText } from "./command-formatters-DEsIufkt.js";
15
- import { a as resolveCodexAppServerForModelProvider, i as resolveCodexSessionPermissionCwd, n as applyCodexSessionPermissionPolicy, r as resolveCodexEffectiveSessionPermissionPolicy } from "./session-permission-policy-BrNH1bjy.js";
16
- import { n as resolveCodexBindingAppServerConnection } from "./binding-connection-BMvPX73o.js";
17
- import { n as prepareCodexAppServerAuthBinding } from "./auth-binding-B4FfbcCg.js";
18
- import { t as persistCodexContextCompactionActivity } from "./context-compaction-activity-BOeJf7yo.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-4Mfq2HDs.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-Dnlfa--w.js";
22
- import { a as runCodexComputerUseLiveTest, n as ensureCodexComputerUse, t as defaultCodexPluginMetadataCache } from "./plugin-metadata-cache-CTCT3w5L.js";
23
- import { asBoolean, asFiniteNumber, asOptionalRecord, hasNonEmptyString, normalizeOptionalString, readNonBlankString, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
1
+ import { $t as isJsonObject, A as TURN_FINALIZE_DRAIN_ABORT_GRACE_MS, B as isCodexAppServerIndeterminateRequestCancellationError, Bt as resolveCodexAppServerAuthProfileIdForAgent, Ct as ensureCodexAppServerClientRuntime, Dt as protectCodexAppServerLiveThread, F as withCodexStartupTimeout, Ft as readRecentCodexRateLimits, H as isCodexAppServerOverloadError, Ht as resolveCodexAppServerFallbackApiKeyCacheKey, I as getCodexAppServerClientInstanceId, It as rememberCodexRateLimitsRead, K as resolveCodexAppServerClientInstanceId, L as isCodexAppServerApprovalRequest, M as isCodexAppServerStartupError, Mt as withAbortableTimeout, P as resolveCodexStartupTimeoutMs, Pt as readCodexRateLimitsRevision, Qt as flattenCodexDynamicToolFunctions, R as isCodexAppServerBrokenPipeError, Rt as resolveCodexAppServerAuthAccountCacheKey, Ut as resolveCodexAppServerPreparedApiKeyCacheKey, V as isCodexAppServerIndeterminateTransportError, W as isCodexAppServerRequestTimeoutError, Wt as resolveCodexAppServerPreparedAuthHandoff, _t as resolveCodexToolAbortTerminalReason, a as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, at as readCodexTurnCompletedNotification, c as getLeasedSharedCodexAppServerClient, ct as hasPendingDynamicToolTerminalDiagnostic, dt as resolveDynamicToolCallTimeoutMs, f as readCodexAppServerClientDesktopGenerationFingerprint, ft as resolveTerminalDynamicToolBatchAction, gt as toCodexDynamicToolProtocolResponse, h as releaseLeasedSharedCodexAppServerClient, ht as toCodexDynamicToolProgressResponse, i as clearSharedCodexAppServerClientIfCurrent, it as readCodexTurn, j as TURN_TERMINAL_SETTLEMENT_TIMEOUT_MS, k as CodexAppServerStartupError, l as getSharedCodexAppServerClient, lt as isDynamicToolTerminalDiagnosticEvent, mt as shouldReleaseTurnAfterTerminalDynamicTool, nt as readCodexDynamicToolCallParams, ot as createCodexElicitationResponse, pt as shouldBlockTerminalReleaseForNonTerminalDynamicToolResult, q as CodexAppServerRpcError, s as createIsolatedCodexAppServerClient, st as handleDynamicToolCallWithTimeout, tt as assertCodexTurnStartResponse, u as isCodexAppServerStartSelectionChangedError, ut as isMatchingDynamicToolTerminalDiagnostic, v as retainSharedCodexAppServerClientIfCurrent, y as retireSharedCodexAppServerClientIfCurrent, z as isCodexAppServerConnectionClosedError, zt as resolveCodexAppServerAuthProfileId } from "./shared-client-DxcfF9KX.js";
2
+ import { C as isCodexPairedNodeRemoteExecPlacementSandbox, D as resolveCodexPluginsPolicy, E as readCodexPluginConfig, F as resolveCodexAppServerHomeDir, O as resolveOpenClawExecPolicyForCodexAppServer, T as isCodexSandboxExecServerEnabled, d as shouldAutoApproveCodexAppServerApprovals, f as withMcpElicitationsApprovalPolicy, h as resolveCodexModelBackedReviewerPolicyContext, l as hasCodexMcpToolApprovalOverrides, m as canUseCodexModelBackedApprovalsReviewerForModel, o as resolveCodexComputerUseConfig, r as resolveCodexAppServerHomeScope, w as isCodexRemoteExecPlacementSandbox, y as readCodexRequirementsToml } from "./config-BhmfC1s6.js";
3
+ import { c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, i as createCodexSessionGenerationSupersededError, l as resolveCodexRunSessionBindingAuthority, u as scopeCodexRunBindingStore } from "./session-binding-BHuTCRZQ.js";
4
+ import { B as readNonNegativeInteger, E as itemStatus, H as splitPlanText, I as readItem, L as readItemString, M as extractRawAssistantText, O as matchesCodexSnapshotTurn, P as readCodexErrorNotificationMessage, T as itemName, V as readNullableString, a as closeCodexStartupClientBestEffort, d as unsubscribeCodexThreadBestEffort, f as CODEX_APP_SERVER_NATIVE_TURN_WAIT_TIMEOUT_MS, g as readCodexNotificationThreadId, h as isCodexNotificationForTurn, j as shouldSynthesizeToolProgressForItem, k as shouldClearTerminalPresentationForNativeItem, l as retireUnsafeCodexTurnClientBestEffort, m as CodexProjectionDiagnostics, o as interruptCodexTurnAndWaitBestEffort, p as getCodexAppServerTurnRouter, r as CodexAppServerUnsafeSubscriptionError, u as terminateCodexBackgroundTerminals } from "./attempt-client-cleanup-DYHcRQAa.js";
5
+ import { a as codexExecutionToolName, c as isPendingOpenClawDynamicToolCompletionNotification, d as isTerminalTurnStatus, f as readCodexNotificationItem, h as updateActiveTurnItemIds, l as isRawFunctionToolOutputCompletionNotification, m as readRawResponseToolCallId, p as readNotificationItemId, r as projectCodexThreadUsageUpdate, s as isCodexTurnAbortMarkerNotification, t as CodexResponseCompletionProjection } from "./event-projector-usage-C5mcrGjp.js";
6
+ import { i as buildCodexPluginAppCacheKey, r as buildCodexAppServerRuntimeFingerprint, s as defaultCodexAppInventoryCache } from "./thread-resume-C7pyVKeW.js";
7
+ import { A as readCodexMcpToolConnectorId, B as emitCodexNativePreToolUseFailureDiagnostic, C as buildDeveloperInstructions, Ct as buildCodexPluginThreadConfig, D as resolveCodexAppServerThreadModelSelection, Dt as mergeCodexThreadConfigs, H as resolveCodexNativeHookRelayTtlMs, I as buildCodexNativeHookRelayConfig, L as buildCodexNativeHookRelayDisabledConfig, M as isCodexAppServerProfilerEnabled, Ot as shouldBuildCodexPluginThreadConfig, P as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, Pt as resolveRecoverableCodexPluginConfigKeys, S as resolveCodexWebSearchPlan, Tt as buildCodexPluginThreadConfigTimeoutFallback, U as scheduleCodexNativeHookRelayUnregister, V as resolveCodexNativeHookRelayEvents, X as loadCodexNativeToolCatalog, Y as hasCodexNativeToolCatalog, _t as neutralizeCodexExplicitMentionSigils, at as filterCodexDynamicTools, bt as resolveCodexContextEngineProjectionReserveTokens, c as assertScheduledCodexAppAuthorityRuntime, d as buildScheduledCodexAppServerConnectionIdentity, f as captureScheduledCodexAppAuthority, ft as resolveCodexDynamicToolsLoadingForRuntime, gt as fitCodexProjectedContextForTurnStart, h as resolveScheduledCodexAppCreatorCaptureDecision, ht as buildCodexContinuityCalibration, i as buildCodexUserInput, j as readCodexMcpToolUiVisibility, l as buildLegacyScheduledCodexAppRecoveryPrompt, lt as isSystemAgentOnlyCodexDynamicToolAllowlist, m as readCurrentCodexScheduledAppPolicy$1, n as buildTurnCollaborationMode, nt as codexLegacyDynamicToolsFingerprint, p as intersectCodexPluginThreadConfigWithScheduledAuthority, pt as buildContextEngineBinding, r as buildTurnStartParams, s as startOrResumeThread, tt as codexDynamicToolsFingerprint, u as buildScheduledCodexAppAuthorityInputFingerprint, vt as projectContextEngineAssemblyForCodex, wt as buildCodexPluginThreadConfigInputFingerprint, xt as resolveCodexContinuityProjectionMaxChars, yt as resolveCodexContextEngineProjectionMaxChars, z as createCodexNativeHookRelay } from "./thread-lifecycle-DAcEJ_Om.js";
8
+ import { l as isIncognitoSessionKey, r as retainCodexAppServerBindingSubscription } from "./thread-ownership-zGokd8i1.js";
9
+ import { a as buildCodexUserPromptMessage, i as mirrorPromptAtTurnStartBestEffort, l as hasCodexMirrorOrigin, n as createCodexAppServerUserMessagePersistenceNotifier, o as promptSnapshot, s as applyCodexTranscriptTaint, t as codexTranscriptMirrorRuntime } from "./transcript-mirror-DTUxFWa_.js";
10
+ import { n as CodexThreadPolicyHandoffError } from "./thread-policy-g4KbJPzp.js";
11
+ import { r as readMirrorIdentity, t as attachCodexMirrorIdentity } from "./upstream-prompt-provenance-LphB8slG.js";
12
+ import { t as CODEX_CONTROL_METHODS } from "./capabilities-dIjNVCYH.js";
13
+ import { a as shouldRefreshCodexRateLimitsForUsageLimitMessage, i as resolveCodexUsageLimitResetAtMs, n as formatCodexUsageLimitErrorMessage } from "./rate-limits-CawQJfUS.js";
14
+ import { a as formatCodexDisplayText } from "./command-formatters-CLHhEB1Q.js";
15
+ import { a as resolveCodexAppServerForModelProvider, i as resolveCodexSessionPermissionCwd, n as applyCodexSessionPermissionPolicy, r as resolveCodexEffectiveSessionPermissionPolicy } from "./session-permission-policy-DeWVTCMj.js";
16
+ import { n as resolveCodexBindingAppServerConnection } from "./binding-connection-E6uDsPlx.js";
17
+ import { n as prepareCodexAppServerAuthBinding } from "./auth-binding-Ds_QVm-X.js";
18
+ import { t as persistCodexContextCompactionActivity } from "./context-compaction-activity-CMla5FPJ.js";
19
+ import { i as createAssistantReasoningMessage, n as createAssistantCommentaryMessage, r as createAssistantMessage, t as createAssistantAsyncMessage } from "./event-projector-assistant-message-DHRFXKbS.js";
20
+ import { a as CodexToolProgressProjection, c as isCodexCommandBearingToolCall, d as sanitizeCodexToolResponse, i as CodexToolTranscriptProjection, l as resolveCodexToolProgressDetailMode, n as CodexEventProjection, o as shouldEmitTranscriptToolProgress, r as emitCodexAgentEvent, s as inferCodexDynamicToolMeta, t as codexNativeSubagentMonitorRuntime, u as sanitizeCodexToolArguments } from "./native-subagent-monitor-CnH147jH.js";
21
+ import { a as requestPluginApprovalOutcome } from "./plugin-approval-roundtrip-CNe-Xrjx.js";
22
+ import { S as shouldWarnCodexDynamicToolBuildStageSummary, T as CodexNativeToolLifecycleProjector, _ as resolveCodexMessageToolProvider, a as emitDynamicToolStartedDiagnostic, b as shouldEnableCodexAppServerNativeToolSurface, c as ensureCodexSandboxExecServerEnvironment, d as createCodexDynamicToolBuildStageTracker, f as disableCodexPluginThreadConfig, g as resolveCodexExternalSandboxPolicyForOpenClawSandbox, h as resolveCodexAppServerHookChannelId, i as emitDynamicToolErrorDiagnostic, l as releaseCodexSandboxExecServerEnvironment, m as resolveCodexAppServerExecutionCwd, n as projectCodexExecutableDynamicTools, o as emitDynamicToolTerminalDiagnostic, p as formatCodexDynamicToolBuildStageSummary, r as routeCodexAppServerElicitationRequest, s as handleCodexAppServerApprovalRequest, t as createCodexDynamicToolBridge, u as buildDynamicTools, w as readBoundedCodexRemoteWorkspaceFile, x as shouldRequireCodexSandboxExecServerEnvironment, y as resolveCodexSandboxEnvironmentSelection } from "./dynamic-tools-BU9tmlYm.js";
23
+ import { t as resolveCodexProviderWebSearchSupport } from "./provider-capabilities-DW5p4k9w.js";
24
+ import { a as getCodexWorkspaceMemoryToolNames, c as readContextEngineThreadBootstrapProjection, d as resolveCodexDeliveryHintPreservedInputRange, f as resolveContextEngineBootstrapProjectionDecision, i as buildCodexWorkspaceBootstrapContext, l as readMirroredSessionHistoryMessages, n as buildCodexSystemPromptReport, r as buildCodexWatchedSessionsContext, s as prependCodexOpenClawPromptContext, t as buildCodexOpenClawPromptContext, u as renderCodexSkillsCollaborationInstructions } from "./attempt-context-DmQUNWwT.js";
25
+ import { n as captureCodexSettledTurnFinalizationContext } from "./settled-turn-context-D3SNWtUn.js";
26
+ import { a as runCodexComputerUseLiveTest, n as ensureCodexComputerUse, t as defaultCodexPluginMetadataCache } from "./plugin-metadata-cache-BFrPhb2c.js";
27
+ import { asOptionalRecord, normalizeOptionalString, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
24
28
  import fs from "node:fs/promises";
25
29
  import path from "node:path";
26
30
  import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
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, buildEmbeddedForegroundPromptContext, buildHarnessContextEngineRuntimeContext, buildWatchedSessionsHarnessContext, callGatewayTool, cancelPendingAgentQuestionForSession, claimPendingAgentQuestionAnswer, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, embeddedAgentLog, emitAgentEvent, emptyAgentHarnessUserInputAnswers, formatErrorMessage, formatFastModeAutoProgressText, getAgentHarnessHookRunner, hasBeforeToolCallPolicy, 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";
29
- import crypto, { createHash } from "node:crypto";
30
- import { markAuthProfileBlockedUntil, resolveAgentDir as resolveAgentDir$1, resolveAgentWorkspaceDir } from "openclaw/plugin-sdk/agent-runtime";
31
- import { resolveAgentConfig as resolveAgentConfig$1 } from "openclaw/plugin-sdk/agent-scope-runtime";
31
+ import { MAX_TIMER_TIMEOUT_MS, asDateTimestampMs, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
32
+ import { AgentHarnessPreflightError, CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, FAST_MODE_AUTO_PROGRESS_KIND, agentHarnessAttemptTerminal, agentHarnessStructuredInput, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentEndSideEffects, bootstrapHarnessContextEngine, buildAgentHookContextChannelFields, buildEmbeddedForegroundPromptContext, buildHarnessContextEngineRuntimeContext, callGatewayTool, cancelPendingAgentQuestionForSession, claimPendingAgentQuestionAnswer, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, detectAndLoadAgentHarnessPromptImages, embeddedAgentLog, emitAgentEvent, emptyAgentHarnessUserInputAnswers, formatErrorMessage, formatFastModeAutoProgressText, getAgentHarnessHookRunner, hasBeforeToolCallPolicy, isActiveHarnessContextEngine, isHostScopedAgentToolActive, loadCodexBundleMcpThreadConfig, materializeRequesterScopedMcpToolsForHarnessRun, prepareHarnessNativeMcpAppPreview, projectAgentHarnessTranscriptMessageForDisplay, resolveAgentDir, resolveAgentHarnessBeforePromptBuildResult, resolveAgentRunAbortLifecycleFields, resolveAttemptFsWorkspaceOnly, resolveContextEngineOwnerPluginId, resolveFastModeForElapsed, resolveSandboxContext, resolveUserPath, runAgentCleanupStep, runAgentEndSideEffects, runAgentHarnessAfterCompactionHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
33
+ import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
34
+ import { createHash } from "node:crypto";
35
+ import { markAuthProfileBlockedUntil, resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
36
+ import { resolveAgentConfig as resolveAgentConfig$1, resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
32
37
  import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
33
38
  import { parseSqliteSessionFileMarker } from "openclaw/plugin-sdk/session-store-runtime";
39
+ import { addAbortListener } from "node:events";
34
40
  import { truncateUtf16Safe, truncateUtf8Prefix } from "openclaw/plugin-sdk/text-utility-runtime";
35
- import { isPathStrictlyInside, root } from "openclaw/plugin-sdk/file-access-runtime";
36
- import { captureFinalCodexCronCreatorToolAllowlist, materializeStaticMcpToolsForScheduledHarnessRun, resolveCodexMcpToolOverridesForAgent } from "openclaw/plugin-sdk/codex-mcp-projection";
37
41
  import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent, emitTrustedDiagnosticEventWithPrivateData, freezeDiagnosticTraceContext, onInternalDiagnosticEvent, resolveDiagnosticModelContentCapturePolicy } from "openclaw/plugin-sdk/diagnostic-runtime";
42
+ import { captureFinalCodexCronCreatorToolAllowlist, formatMcpCodexApprovalRemedy, materializeStaticMcpToolsForHarnessRun, resolveCodexMcpToolOverridesForAgent, resolveCodexTtsProvenanceTransfer } from "openclaw/plugin-sdk/codex-mcp-projection";
43
+ import { hasPromptImageInput } from "openclaw/plugin-sdk/session-transcript-runtime";
38
44
  import { Buffer as Buffer$1 } from "node:buffer";
39
45
  import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
40
- import { prepareMemorySystemPromptAddition } from "openclaw/plugin-sdk/core";
46
+ import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-local-roots";
41
47
  import { isSilentReplyPayloadText } from "openclaw/plugin-sdk/reply-chunking";
42
48
  import { generatedImageAssetFromBase64 } from "openclaw/plugin-sdk/image-generation";
43
49
  import { resolveGeneratedMediaMaxBytes } from "openclaw/plugin-sdk/media-generation-runtime";
44
50
  import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
45
51
  import { stripInvisibleUnicode } from "openclaw/plugin-sdk/web-content-extractor";
46
- import { MESSAGE_TOOL_DELIVERY_HINTS } from "openclaw/plugin-sdk/message-tool-delivery-hints";
47
52
  //#region extensions/codex/src/app-server/attempt-steering.ts
48
53
  /**
49
54
  * Debounced steering queue for forwarding user messages to an active Codex
@@ -69,6 +74,12 @@ function createCodexSteeringQueue(params) {
69
74
  let sendChain = Promise.resolve();
70
75
  let sealedError;
71
76
  let closedError;
77
+ const assertActive = () => {
78
+ const unavailableError = closedError ?? sealedError;
79
+ if (unavailableError) throw unavailableError;
80
+ params.signal.throwIfAborted();
81
+ params.assertActive();
82
+ };
72
83
  const clearBatchTimer = () => {
73
84
  if (batchTimer) {
74
85
  clearTimeout(batchTimer);
@@ -121,19 +132,20 @@ function createCodexSteeringQueue(params) {
121
132
  const sendBatch = async (items) => {
122
133
  const liveItems = items.filter((item) => !item.settled);
123
134
  if (liveItems.length === 0) return;
124
- const unavailableError = closedError ?? sealedError ?? (params.signal.aborted ? /* @__PURE__ */ new Error("codex app-server steering queue aborted") : void 0);
125
- if (unavailableError) {
126
- for (const item of liveItems) rejectItem(item, unavailableError);
127
- throw unavailableError;
128
- }
129
- const clientUserMessageId = `openclaw:${params.turnId}:steer:${++batchSequence}`;
130
- const batch = { items: liveItems };
131
- dispatchedBatches.set(clientUserMessageId, batch);
135
+ let clientUserMessageId;
132
136
  try {
137
+ assertActive();
138
+ const input = [];
139
+ for (const item of liveItems) {
140
+ input.push(...await params.prepareMessage(item.text, item));
141
+ assertActive();
142
+ }
143
+ clientUserMessageId = `openclaw:${params.turnId}:steer:${++batchSequence}`;
144
+ dispatchedBatches.set(clientUserMessageId, { items: liveItems });
133
145
  await params.client.request("turn/steer", {
134
146
  threadId: params.threadId,
135
147
  expectedTurnId: params.turnId,
136
- input: liveItems.flatMap((item) => buildCodexUserInput(item.text, item.images)),
148
+ input,
137
149
  clientUserMessageId
138
150
  }, {
139
151
  timeoutMs: params.requestTimeoutMs,
@@ -141,8 +153,8 @@ function createCodexSteeringQueue(params) {
141
153
  });
142
154
  for (const item of liveItems) reportItemAcceptance(item, true);
143
155
  } catch (error) {
144
- dispatchedBatches.delete(clientUserMessageId);
145
- const acceptedUnconfirmed = isCodexAppServerIndeterminateRequestCancellationError(error) || isCodexAppServerIndeterminateTransportError(error);
156
+ if (clientUserMessageId) dispatchedBatches.delete(clientUserMessageId);
157
+ const acceptedUnconfirmed = clientUserMessageId !== void 0 && (isCodexAppServerIndeterminateRequestCancellationError(error) || isCodexAppServerIndeterminateTransportError(error));
146
158
  for (const item of liveItems) {
147
159
  if (acceptedUnconfirmed) reportItemAcceptance(item, true);
148
160
  rejectItem(item, error);
@@ -176,15 +188,12 @@ function createCodexSteeringQueue(params) {
176
188
  rejectDelivery = reject;
177
189
  });
178
190
  const item = {
191
+ ...options,
179
192
  acceptance: "open",
180
193
  text,
181
- images: options?.images,
182
- isInboundUserMessage: options?.isInboundUserMessage,
183
- onQueueAccepted: options?.onQueueAccepted,
184
194
  resolve: resolveDelivery,
185
195
  reject: rejectDelivery,
186
- settled: false,
187
- userTurnTranscriptRecorder: options?.userTurnTranscriptRecorder
196
+ settled: false
188
197
  };
189
198
  pendingMessages.add(item);
190
199
  return {
@@ -196,10 +205,11 @@ function createCodexSteeringQueue(params) {
196
205
  if (params.signal.aborted) abortQueue();
197
206
  return {
198
207
  async queue(text, options) {
199
- const unavailableError = closedError ?? sealedError ?? (params.signal.aborted ? /* @__PURE__ */ new Error("codex app-server steering queue aborted") : void 0);
200
- if (unavailableError) {
208
+ try {
209
+ assertActive();
210
+ } catch (error) {
201
211
  options?.onQueueAccepted?.(false);
202
- throw unavailableError;
212
+ throw error;
203
213
  }
204
214
  const { item, delivery } = createPendingMessage(text, options);
205
215
  batchedMessages.push(item);
@@ -243,20 +253,20 @@ function normalizeCodexSteerDebounceMs(value) {
243
253
  //#endregion
244
254
  //#region extensions/codex/src/app-server/event-projector-assistant.ts
245
255
  var CodexAssistantProjection = class {
246
- constructor(params, emitAgentEvent, matchesToolProgressEcho, nextTranscriptTimestamp) {
256
+ constructor(params, emitAgentEvent, matchesToolProgressEcho, nextTranscriptTimestamp, checkpointCommentary) {
247
257
  this.params = params;
248
258
  this.emitAgentEvent = emitAgentEvent;
249
259
  this.matchesToolProgressEcho = matchesToolProgressEcho;
250
260
  this.nextTranscriptTimestamp = nextTranscriptTimestamp;
261
+ this.checkpointCommentary = checkpointCommentary;
251
262
  this.assistantTextByItem = /* @__PURE__ */ new Map();
252
263
  this.assistantItemOrder = [];
253
264
  this.assistantTimestampByItem = /* @__PURE__ */ new Map();
254
265
  this.assistantPhaseByItem = /* @__PURE__ */ new Map();
255
266
  this.assistantDeliveryByItem = /* @__PURE__ */ new Map();
256
267
  this.latestTerminalAssistantCandidateSuperseded = false;
257
- this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
258
268
  this.terminalAssistantCandidateEarlierActiveItemIds = /* @__PURE__ */ new Set();
259
- this.lastCommentaryProgressTextByItem = /* @__PURE__ */ new Map();
269
+ this.lastCommentaryProgressEventByItem = /* @__PURE__ */ new Map();
260
270
  this.lastAnswerCandidateEventByItem = /* @__PURE__ */ new Map();
261
271
  this.pendingRawCommentaryEchoes = 0;
262
272
  this.rawPromotedAssistantItemIds = /* @__PURE__ */ new Set();
@@ -265,27 +275,7 @@ var CodexAssistantProjection = class {
265
275
  this.persistableAssistantBarrier = 0;
266
276
  this.persistedAssistantBoundary = false;
267
277
  this.persistableAssistantBarrierItemIds = /* @__PURE__ */ new Set();
268
- }
269
- hasCompletedTerminalAssistantText(completedItemIds) {
270
- const latestCompletedItemId = this.latestCompletedTerminalAssistantItemId;
271
- if (!latestCompletedItemId) return false;
272
- const finalItem = this.resolveFinalAssistantTextItem();
273
- return this.latestCompletedItemId === latestCompletedItemId && finalItem?.itemId === latestCompletedItemId && completedItemIds.has(latestCompletedItemId);
274
- }
275
- getLatestTerminalAssistantCandidate() {
276
- const itemId = this.latestTerminalAssistantCandidateItemId;
277
- if (!itemId) return;
278
- const text = this.assistantTextByItem.get(itemId)?.trim();
279
- return {
280
- itemId,
281
- hasText: Boolean(text && !this.isToolProgressEchoText(itemId, text))
282
- };
283
- }
284
- hasLatestTerminalAssistantCandidateText() {
285
- return !this.latestTerminalAssistantCandidateSuperseded && this.getLatestTerminalAssistantCandidate()?.hasText === true;
286
- }
287
- canReleaseLatestTerminalAssistantAfterToolHandoff() {
288
- return this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff && this.hasLatestTerminalAssistantCandidateText();
278
+ this.completedAssistantItemIds = /* @__PURE__ */ new Set();
289
279
  }
290
280
  handleNotification(method, params) {
291
281
  if (method === "model/rerouted") this.responseModel = readStringField(params, "toModel") ?? this.responseModel;
@@ -309,12 +299,13 @@ var CodexAssistantProjection = class {
309
299
  if (isCommentary) {
310
300
  this.emitCommentaryProgress({
311
301
  itemId,
312
- text
302
+ text,
303
+ phase: "update"
313
304
  });
314
305
  return;
315
306
  }
316
- if (this.isFinalAnswerAssistantItem(itemId)) this.emitAnswerCandidate(itemId, "candidate");
317
- const knownFinalAnswer = this.shouldStreamAssistantPartial(itemId);
307
+ const knownFinalAnswer = this.isFinalAnswerAssistantItem(itemId);
308
+ if (knownFinalAnswer) this.emitAnswerCandidate(itemId, "candidate");
318
309
  const replace = this.streamedPartialAssistantItemId !== void 0 && this.streamedPartialAssistantItemId !== itemId;
319
310
  if (replace && (!knownFinalAnswer || this.streamedPartialAssistantItemReplaceable)) this.streamedPartialAssistantItemReplaceable = true;
320
311
  else if (this.streamedPartialAssistantItemId === void 0) this.streamedPartialAssistantItemReplaceable = !knownFinalAnswer;
@@ -346,12 +337,11 @@ var CodexAssistantProjection = class {
346
337
  }
347
338
  }
348
339
  recordItemCompleted(item, itemId, activeItemIds) {
340
+ if (itemId && item?.type === "agentMessage") this.completedAssistantItemIds.add(itemId);
349
341
  this.noteNativeWorkBarrier(item);
350
342
  this.rememberAssistantPhase(item);
351
343
  if (item?.type === "agentMessage" && itemId && !this.isNonTerminalAssistantItem(itemId) && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
352
- if (itemId && !this.isNonTerminalAssistantItem(itemId)) this.latestCompletedItemId = itemId;
353
344
  if (item?.type === "agentMessage" && !this.isNonTerminalAssistantItem(item.id)) {
354
- this.latestCompletedTerminalAssistantItemId = item.id;
355
345
  this.markLatestTerminalAssistantCandidate(item.id, activeItemIds);
356
346
  this.pendingRawTerminalAssistantEchoItemId = item.id;
357
347
  } else if (itemId && !this.isNonTerminalAssistantItem(itemId)) {
@@ -364,7 +354,8 @@ var CodexAssistantProjection = class {
364
354
  if (item.text && this.isCommentaryAssistantItem(item.id)) {
365
355
  this.emitCommentaryProgress({
366
356
  itemId: item.id,
367
- text: item.text
357
+ text: item.text,
358
+ phase: "end"
368
359
  });
369
360
  this.pendingRawCommentaryEchoes += 1;
370
361
  } else if (item.text && !this.isAsyncAssistantItem(item.id) && this.isFinalAnswerAssistantItem(item.id)) this.emitAnswerCandidate(item.id, "candidate");
@@ -372,6 +363,7 @@ var CodexAssistantProjection = class {
372
363
  }
373
364
  }
374
365
  recordSnapshotItem(item) {
366
+ if (item.type === "agentMessage") this.completedAssistantItemIds.add(item.id);
375
367
  this.rememberAssistantPhase(item);
376
368
  if (item.type === "agentMessage" && typeof item.text === "string") {
377
369
  this.rememberAssistantItem(item.id);
@@ -387,10 +379,7 @@ var CodexAssistantProjection = class {
387
379
  const pendingTerminalAssistantEchoItemId = this.pendingRawTerminalAssistantEchoItemId;
388
380
  const isPendingTerminalAssistantEcho = role === "assistant" && phase !== "commentary" && pendingTerminalAssistantEchoItemId !== void 0 && (rawItemId === void 0 || rawItemId === pendingTerminalAssistantEchoItemId);
389
381
  if (pendingTerminalAssistantEchoItemId !== void 0 && !isPendingTerminalAssistantEcho) this.pendingRawTerminalAssistantEchoItemId = void 0;
390
- if (!isPendingTerminalAssistantEcho) {
391
- this.latestCompletedItemId = void 0;
392
- this.markTerminalAssistantCandidateSupersededBy(rawItemId);
393
- }
382
+ if (!isPendingTerminalAssistantEcho) this.markTerminalAssistantCandidateSupersededBy(rawItemId);
394
383
  if (role !== "assistant") return;
395
384
  if (phase === "commentary" && this.pendingRawCommentaryEchoes > 0) {
396
385
  this.pendingRawCommentaryEchoes -= 1;
@@ -410,15 +399,17 @@ var CodexAssistantProjection = class {
410
399
  const isIdlessTerminalAssistantAfterCompletedWork = candidateWasSupersededBeforeRaw && rawItemId === void 0 && pendingTerminalAssistantEchoItemId === void 0 && activeItemIds.size === 0;
411
400
  if (text && phase !== "commentary" && candidateWasSupersededBeforeRaw && itemId !== this.streamedPartialAssistantItemId && !isIdlessTerminalAssistantAfterCompletedWork) return;
412
401
  if (phase) this.assistantPhaseByItem.set(itemId, phase);
402
+ this.completedAssistantItemIds.add(itemId);
413
403
  this.rememberAssistantItem(itemId);
414
404
  this.assistantTextByItem.set(itemId, text);
415
405
  if (!text) return;
416
406
  this.rawPromotedAssistantItemIds.add(itemId);
417
407
  if (phase === "commentary") this.emitCommentaryProgress({
418
408
  itemId,
419
- text
409
+ text,
410
+ phase: "end"
420
411
  });
421
- else this.markLatestTerminalAssistantCandidate(itemId, activeItemIds, { canReleaseAfterToolHandoff: isIdlessTerminalAssistantAfterCompletedWork });
412
+ else this.markLatestTerminalAssistantCandidate(itemId, activeItemIds);
422
413
  }
423
414
  collectAssistantTexts() {
424
415
  const afterHandoff = this.collectPersistableAssistantTexts(this.persistableAssistantBarrier);
@@ -433,16 +424,18 @@ var CodexAssistantProjection = class {
433
424
  }
434
425
  collectCommentaryMessages() {
435
426
  return this.assistantItemOrder.flatMap((itemId) => {
436
- if (!this.isCommentaryAssistantItem(itemId)) return [];
437
- const text = this.assistantTextByItem.get(itemId)?.trim();
438
- const timestamp = this.assistantTimestampByItem.get(itemId);
439
- if (!text || timestamp === void 0) return [];
440
- return [{
427
+ const message = this.readCommentaryMessage(itemId);
428
+ return message ? [{
441
429
  itemId,
442
- message: createAssistantCommentaryMessage(this.params, text, itemId, timestamp)
443
- }];
430
+ message
431
+ }] : [];
444
432
  });
445
433
  }
434
+ readCommentaryMessage(itemId) {
435
+ const text = this.assistantTextByItem.get(itemId)?.trim();
436
+ const timestamp = this.assistantTimestampByItem.get(itemId);
437
+ return this.isCommentaryAssistantItem(itemId) && text && timestamp !== void 0 ? createAssistantCommentaryMessage(this.params, text, itemId, timestamp) : void 0;
438
+ }
446
439
  collectAsyncMessages() {
447
440
  return this.assistantItemOrder.flatMap((itemId) => {
448
441
  if (!this.isAsyncAssistantItem(itemId)) return [];
@@ -455,19 +448,20 @@ var CodexAssistantProjection = class {
455
448
  }];
456
449
  });
457
450
  }
458
- createCompletedAssistantBoundaryMessage(completedItemIds, options) {
459
- const itemId = this.latestCompletedTerminalAssistantItemId;
460
- const text = itemId ? this.assistantTextByItem.get(itemId)?.trim() : void 0;
461
- const itemIndex = itemId ? this.assistantItemOrder.indexOf(itemId) : -1;
462
- if (!itemId || !completedItemIds.has(itemId) || itemIndex < this.persistableAssistantBarrier || !text || isSilentReplyPayloadText(text) || this.isToolProgressEchoText(itemId, text)) return;
463
- const message = this.createAssistantMessage(text, options);
464
- return {
465
- itemId,
466
- message: {
467
- ...message,
468
- timestamp: this.assistantTimestampByItem.get(itemId) ?? message.timestamp
469
- }
470
- };
451
+ collectCompletedAssistantMessages(completedItemIds, options) {
452
+ return this.assistantItemOrder.flatMap((itemId) => {
453
+ const text = this.assistantTextByItem.get(itemId)?.trim();
454
+ if (!completedItemIds.has(itemId) || this.isNonTerminalAssistantItem(itemId) || !text || isSilentReplyPayloadText(text) || this.isToolProgressEchoText(itemId, text)) return [];
455
+ const message = this.createAssistantMessage(text, options);
456
+ const timestamp = this.assistantTimestampByItem.get(itemId) ?? message.timestamp;
457
+ return [{
458
+ itemId,
459
+ message: {
460
+ ...message,
461
+ timestamp
462
+ }
463
+ }];
464
+ });
471
465
  }
472
466
  markAssistantBoundaryPersisted(itemId) {
473
467
  const index = this.assistantItemOrder.indexOf(itemId);
@@ -526,9 +520,6 @@ var CodexAssistantProjection = class {
526
520
  responseModel: this.responseModel
527
521
  } : message;
528
522
  }
529
- createAssistantMirrorMessage(title, text) {
530
- return createAssistantMirrorMessage(this.params, title, text);
531
- }
532
523
  rememberAssistantPhase(item) {
533
524
  if (item?.type !== "agentMessage") return;
534
525
  const phase = readItemString(item, "phase");
@@ -548,20 +539,21 @@ var CodexAssistantProjection = class {
548
539
  isFinalAnswerAssistantItem(itemId) {
549
540
  return this.assistantPhaseByItem.get(itemId) === "final_answer";
550
541
  }
551
- shouldStreamAssistantPartial(itemId) {
552
- return this.assistantPhaseByItem.get(itemId) === "final_answer";
553
- }
554
542
  emitCommentaryProgress(params) {
555
543
  const progressText = params.text.trim();
556
- if (!progressText || this.lastCommentaryProgressTextByItem.get(params.itemId) === progressText) return;
557
- this.lastCommentaryProgressTextByItem.set(params.itemId, progressText);
544
+ const previous = this.lastCommentaryProgressEventByItem.get(params.itemId);
545
+ if (!progressText || previous?.phase === params.phase && previous.text === progressText) return;
546
+ this.lastCommentaryProgressEventByItem.set(params.itemId, {
547
+ phase: params.phase,
548
+ text: progressText
549
+ });
558
550
  this.emitAgentEvent({
559
551
  stream: "item",
560
552
  data: {
561
553
  itemId: params.itemId,
562
554
  kind: "preamble",
563
555
  title: "Preamble",
564
- phase: "update",
556
+ phase: params.phase,
565
557
  progressText,
566
558
  source: "codex-app-server"
567
559
  }
@@ -574,9 +566,12 @@ var CodexAssistantProjection = class {
574
566
  this.supersedeVisibleAnswerCandidate();
575
567
  this.visibleAnswerCandidateItemId = itemId;
576
568
  }
577
- const signature = `${status}\0${text}`;
578
- if (this.lastAnswerCandidateEventByItem.get(itemId) === signature) return;
579
- this.lastAnswerCandidateEventByItem.set(itemId, signature);
569
+ const previous = this.lastAnswerCandidateEventByItem.get(itemId);
570
+ if (previous?.status === status && previous.text === text) return;
571
+ this.lastAnswerCandidateEventByItem.set(itemId, {
572
+ status,
573
+ text
574
+ });
580
575
  this.emitAgentEvent({
581
576
  stream: "item",
582
577
  data: {
@@ -597,10 +592,9 @@ var CodexAssistantProjection = class {
597
592
  this.emitAnswerCandidate(itemId, "superseded");
598
593
  this.visibleAnswerCandidateItemId = void 0;
599
594
  }
600
- markLatestTerminalAssistantCandidate(itemId, activeItemIds, options) {
595
+ markLatestTerminalAssistantCandidate(itemId, activeItemIds) {
601
596
  this.latestTerminalAssistantCandidateItemId = itemId;
602
597
  this.latestTerminalAssistantCandidateSuperseded = false;
603
- this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = options?.canReleaseAfterToolHandoff === true;
604
598
  this.terminalAssistantCandidateEarlierActiveItemIds = new Set(activeItemIds);
605
599
  }
606
600
  markTerminalAssistantCandidateSupersededBy(itemId, options) {
@@ -610,7 +604,6 @@ var CodexAssistantProjection = class {
610
604
  return;
611
605
  }
612
606
  this.latestTerminalAssistantCandidateSuperseded = true;
613
- this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
614
607
  this.terminalAssistantCandidateEarlierActiveItemIds.clear();
615
608
  this.supersedeVisibleAnswerCandidate();
616
609
  }
@@ -655,9 +648,15 @@ var CodexAssistantProjection = class {
655
648
  this.persistableAssistantBarrier = this.assistantItemOrder.length;
656
649
  }
657
650
  rememberAssistantItem(itemId) {
658
- if (!itemId || this.assistantItemOrder.includes(itemId)) return;
659
- this.assistantItemOrder.push(itemId);
660
- this.assistantTimestampByItem.set(itemId, this.nextTranscriptTimestamp());
651
+ if (!itemId) return;
652
+ if (!this.assistantTimestampByItem.has(itemId)) {
653
+ this.assistantItemOrder.push(itemId);
654
+ this.assistantTimestampByItem.set(itemId, this.nextTranscriptTimestamp());
655
+ }
656
+ if (this.isCommentaryAssistantItem(itemId)) this.checkpointCommentary?.(itemId, {
657
+ read: () => this.readCommentaryMessage(itemId),
658
+ ready: () => this.completedAssistantItemIds.has(itemId)
659
+ });
661
660
  }
662
661
  createAsyncDelivery(itemId) {
663
662
  if (!this.isAsyncAssistantItem(itemId)) return;
@@ -1005,49 +1004,29 @@ function reasoningMethodOrder(method) {
1005
1004
  const attemptTerminal = agentHarnessAttemptTerminal;
1006
1005
  //#endregion
1007
1006
  //#region extensions/codex/src/app-server/event-projector-snapshot.ts
1008
- const CODEX_META_KEY$1 = "__openclaw";
1009
- function readTurnTaintMetadata(message) {
1010
- return asOptionalRecord(CODEX_META_KEY$1 in message ? message[CODEX_META_KEY$1] : void 0);
1011
- }
1012
- function applyStickyTurnTaint(messages) {
1013
- let tainted = false;
1014
- return messages.map((message) => {
1015
- if (message.role === "user") {
1016
- tainted = false;
1017
- return message;
1018
- }
1019
- const metadata = readTurnTaintMetadata(message);
1020
- tainted ||= metadata?.turnTainted === true || metadata?.resultContentSource === "network";
1021
- return message.role === "assistant" && tainted ? {
1022
- ...message,
1023
- __openclaw: {
1024
- ...metadata,
1025
- turnTainted: true
1026
- }
1027
- } : message;
1028
- });
1029
- }
1030
1007
  function buildCodexMessagesSnapshot(params) {
1031
1008
  const messages = promptSnapshot(params.runParams, params.turnId, params.upstreamUserText);
1032
- if (params.reasoningText) messages.push(attachCodexMirrorIdentity(params.createAssistantMirrorMessage("Codex reasoning", params.reasoningText), `${params.turnId}:reasoning`));
1009
+ if (params.reasoningText) messages.push(attachCodexMirrorIdentity(createAssistantReasoningMessage(params.runParams, params.reasoningText), `${params.turnId}:reasoning`));
1033
1010
  const commentaryMessages = params.runParams.config?.ui?.prefs?.chatPersistCommentary === false ? [] : params.commentaryMessages.map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:commentary:${itemId}`));
1034
1011
  const asyncMessages = params.asyncMessages.map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:async:${itemId}`));
1035
1012
  const visibleWorkMessages = [
1036
1013
  ...commentaryMessages,
1037
1014
  ...asyncMessages,
1015
+ ...(params.assistantMessages ?? []).map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:assistant:${itemId}`)),
1038
1016
  ...params.toolMessages
1039
1017
  ].toSorted((left, right) => (asDateTimestampMs(left.timestamp) ?? 0) - (asDateTimestampMs(right.timestamp) ?? 0));
1040
1018
  messages.push(...visibleWorkMessages);
1041
- if (params.lastAssistant) messages.push(attachCodexMirrorIdentity(params.lastAssistant, params.lastAssistantIdentity ?? `${params.turnId}:assistant`));
1042
- return applyStickyTurnTaint(messages).map((message) => projectAgentHarnessTranscriptMessageForDisplay({
1019
+ if (params.lastAssistant) messages.push(attachCodexMirrorIdentity(params.lastAssistant, `${params.turnId}:assistant`));
1020
+ const taint = { tainted: false };
1021
+ return messages.map((message) => projectAgentHarnessTranscriptMessageForDisplay({
1043
1022
  hidden: params.runParams.trigger === "memory",
1044
- message
1023
+ message: applyCodexTranscriptTaint(message, taint)
1045
1024
  }));
1046
1025
  }
1047
1026
  function buildCodexSteeringMessagesSnapshot(params) {
1048
1027
  const asyncMessages = params.assistantProjection.collectAsyncMessages().filter(({ itemId }) => params.completedItemIds.has(itemId));
1049
1028
  const commentaryMessages = params.assistantProjection.collectCommentaryMessages().filter(({ itemId }) => params.completedItemIds.has(itemId));
1050
- const assistantBoundary = params.assistantProjection.createCompletedAssistantBoundaryMessage(params.completedItemIds, {
1029
+ const assistantMessages = params.assistantProjection.collectCompletedAssistantMessages(params.completedItemIds, {
1051
1030
  tokenUsage: void 0,
1052
1031
  aborted: false,
1053
1032
  promptError: void 0
@@ -1060,12 +1039,11 @@ function buildCodexSteeringMessagesSnapshot(params) {
1060
1039
  reasoningText: void 0,
1061
1040
  asyncMessages,
1062
1041
  commentaryMessages,
1042
+ assistantMessages,
1063
1043
  toolMessages: params.toolMessages,
1064
- lastAssistant: assistantBoundary?.message,
1065
- ...assistantBoundary ? { lastAssistantIdentity: `${params.turnId}:assistant:${assistantBoundary.itemId}` } : {},
1066
- createAssistantMirrorMessage: (title, text) => params.assistantProjection.createAssistantMirrorMessage(title, text)
1044
+ lastAssistant: void 0
1067
1045
  }).filter((message) => message.role !== "user"),
1068
- ...assistantBoundary ? { assistantBoundaryItemId: assistantBoundary.itemId } : {}
1046
+ assistantBoundaryItemId: assistantMessages.at(-1)?.itemId
1069
1047
  };
1070
1048
  }
1071
1049
  //#endregion
@@ -1111,8 +1089,7 @@ function buildCodexAttemptResult(input) {
1111
1089
  asyncMessages,
1112
1090
  commentaryMessages,
1113
1091
  toolMessages: input.toolTranscriptProjection.transcriptMessages,
1114
- lastAssistant,
1115
- createAssistantMirrorMessage: (title, text) => input.assistantProjection.createAssistantMirrorMessage(title, text)
1092
+ lastAssistant
1116
1093
  });
1117
1094
  const turnFailed = input.completedTurn?.status === "failed";
1118
1095
  const promptError = input.promptError ?? storedMissingToolResultError ?? (turnFailed ? input.completedTurn?.error?.message ?? "codex app-server turn failed" : null);
@@ -1125,7 +1102,9 @@ function buildCodexAttemptResult(input) {
1125
1102
  });
1126
1103
  const toolMetas = input.toolProgressProjection.toolMetas;
1127
1104
  const hadPotentialSideEffects = input.toolTelemetry.didSendViaMessagingTool || Boolean(input.toolTelemetry.successfulCronAdds || input.toolTelemetry.acceptedSessionSpawns?.length) || input.generatedMediaProjection.hasGeneratedMedia() || input.toolProgressProjection.hasPotentialSideEffects;
1128
- return {
1105
+ const sentMediaUrls = new Set(input.toolTelemetry.messagingToolSentMediaUrls.map((url) => url.trim()));
1106
+ const toolAutoDeliveryMediaUrls = input.toolTelemetry.toolAutoDeliveryMediaUrls?.filter((url) => !sentMediaUrls.has(url.trim()));
1107
+ const result = {
1129
1108
  terminal: attemptTerminal.normalize({
1130
1109
  aborted: input.aborted,
1131
1110
  promptError,
@@ -1172,8 +1151,83 @@ function buildCodexAttemptResult(input) {
1172
1151
  yieldDetected: input.yieldDetected || false,
1173
1152
  didSendDeterministicApprovalPrompt: input.guardianReviewCount > 0 ? false : void 0
1174
1153
  };
1154
+ const transferTtsProvenance = resolveCodexTtsProvenanceTransfer(input.runParams.hostCapabilities);
1155
+ for (const toolResult of input.toolTelemetry.coreTtsToolResults ?? []) transferTtsProvenance?.(toolResult, result, toolAutoDeliveryMediaUrls ?? []);
1156
+ return result;
1175
1157
  }
1176
1158
  //#endregion
1159
+ //#region extensions/codex/src/app-server/transcript-checkpoint.ts
1160
+ /** Commits completed work in receipt order without waiting for the enclosing turn. */
1161
+ var CodexTranscriptCheckpoint = class {
1162
+ constructor(params, threadId, turnId) {
1163
+ this.params = params;
1164
+ this.threadId = threadId;
1165
+ this.turnId = turnId;
1166
+ this.pending = [];
1167
+ this.commentaryItemIds = /* @__PURE__ */ new Set();
1168
+ this.lastTimestamp = 0;
1169
+ this.writing = Promise.resolve();
1170
+ this.tainted = false;
1171
+ this.closed = false;
1172
+ this.nextTimestamp = () => {
1173
+ this.lastTimestamp = Math.max(Date.now(), this.lastTimestamp + 1);
1174
+ return this.lastTimestamp;
1175
+ };
1176
+ this.enqueueCommentary = (itemId, entry) => {
1177
+ if (this.params.config?.ui?.prefs?.chatPersistCommentary === false || this.commentaryItemIds.has(itemId)) return;
1178
+ this.commentaryItemIds.add(itemId);
1179
+ this.enqueue({
1180
+ ...entry,
1181
+ read: () => {
1182
+ const message = entry.read();
1183
+ return message ? attachCodexMirrorIdentity(message, `${this.turnId}:commentary:${itemId}`) : void 0;
1184
+ }
1185
+ });
1186
+ };
1187
+ this.enqueue = (entry) => {
1188
+ if (this.params.sessionTarget && !this.closed) this.pending.push(entry);
1189
+ };
1190
+ }
1191
+ flush(close = false) {
1192
+ if (this.closed) return this.writing;
1193
+ this.closed = close;
1194
+ this.writing = this.writing.then(async () => {
1195
+ const blocked = close ? -1 : this.pending.findIndex((entry) => entry.ready?.() === false);
1196
+ const count = blocked < 0 ? this.pending.length : blocked;
1197
+ if (count === 0) return;
1198
+ const taint = { tainted: this.tainted };
1199
+ const messages = this.pending.slice(0, count).flatMap((entry) => {
1200
+ const message = entry.read();
1201
+ return message ? [projectAgentHarnessTranscriptMessageForDisplay({
1202
+ hidden: this.params.trigger === "memory",
1203
+ message: applyCodexTranscriptTaint(message, taint)
1204
+ })] : [];
1205
+ });
1206
+ try {
1207
+ await codexTranscriptMirrorRuntime.mirror({
1208
+ ...this.params.sessionTarget,
1209
+ sessionId: this.params.sessionId,
1210
+ cwd: this.params.workspaceDir,
1211
+ messages,
1212
+ idempotencyScope: `codex-app-server:${this.threadId}`,
1213
+ runId: this.params.runId,
1214
+ runMirrorIdentityPrefix: `${this.turnId}:`,
1215
+ config: this.params.config
1216
+ });
1217
+ this.pending.splice(0, count);
1218
+ this.tainted = taint.tainted;
1219
+ } catch (error) {
1220
+ embeddedAgentLog.warn("failed to checkpoint codex app-server transcript", {
1221
+ runId: this.params.runId,
1222
+ sessionId: this.params.sessionId,
1223
+ error: formatErrorMessage(error)
1224
+ });
1225
+ }
1226
+ });
1227
+ return this.writing;
1228
+ }
1229
+ };
1230
+ //#endregion
1177
1231
  //#region extensions/codex/src/app-server/usage-limit-error.ts
1178
1232
  /**
1179
1233
  * Enriches Codex usage-limit failures with current rate-limit information and
@@ -1183,6 +1237,10 @@ const CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS = 5e3;
1183
1237
  function createCodexUsageLimitPromptError(message) {
1184
1238
  return Object.assign(new Error(message), { status: 429 });
1185
1239
  }
1240
+ function resolveCodexPromptError(source) {
1241
+ const usageLimitMessage = formatCodexUsageLimitErrorMessage(source);
1242
+ return usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : source.message ?? void 0;
1243
+ }
1186
1244
  function isCodexUsageLimitPromptError(error) {
1187
1245
  return error instanceof Error && "status" in error && error.status === 429;
1188
1246
  }
@@ -1322,13 +1380,14 @@ var CodexAppServerEventProjector = class {
1322
1380
  this.activeCompactionItemIds = /* @__PURE__ */ new Set();
1323
1381
  this.terminalPresentationClearedItemIds = /* @__PURE__ */ new Set();
1324
1382
  this.nativeToolOutcomeOrdinals = /* @__PURE__ */ new Map();
1383
+ this.projectionClosed = false;
1325
1384
  this.settledTurnFailureFinalizationAllowed = false;
1326
1385
  this.promptErrorSource = null;
1327
1386
  this.synthesizedMissingToolResultError = null;
1328
1387
  this.aborted = false;
1329
1388
  this.responseCompletions = new CodexResponseCompletionProjection();
1330
1389
  this.completedCompactionCount = 0;
1331
- this.lastTranscriptTimestamp = 0;
1390
+ this.transcriptCheckpoint = new CodexTranscriptCheckpoint(params, threadId, turnId);
1332
1391
  this.asyncDeliveryProjection = new CodexAsyncDeliveryProjection(params, threadId, turnId, options);
1333
1392
  this.contextTokens = options.initialContextTokens;
1334
1393
  this.contextTokensSource = options.initialContextTokens === void 0 ? void 0 : "resolved";
@@ -1341,22 +1400,26 @@ var CodexAppServerEventProjector = class {
1341
1400
  signal: options.runAbortSignal
1342
1401
  });
1343
1402
  this.toolProgressProjection = new CodexToolProgressProjection(params);
1344
- this.toolTranscriptProjection = new CodexToolTranscriptProjection(params, threadId, turnId, this.toolProgressProjection, () => this.nextTranscriptTimestamp(), {
1403
+ this.toolTranscriptProjection = new CodexToolTranscriptProjection(params, threadId, turnId, this.toolProgressProjection, this.transcriptCheckpoint.nextTimestamp, {
1345
1404
  nativePostToolUseRelayEnabled: options.nativePostToolUseRelayEnabled,
1346
1405
  prepareNativeMcpAppResultDetails: options.prepareNativeMcpAppResultDetails,
1347
- trajectoryRecorder: options.trajectoryRecorder
1406
+ trajectoryRecorder: options.trajectoryRecorder,
1407
+ checkpointMessage: this.transcriptCheckpoint.enqueue
1348
1408
  });
1349
1409
  this.eventProjection = new CodexEventProjection(threadId, turnId, (event) => this.emitAgentEvent(event), this.toolProgressProjection, this.toolTranscriptProjection, options.onNativeToolResultRecorded);
1350
- this.assistantProjection = new CodexAssistantProjection(params, (event) => this.emitAgentEvent(event), (text) => this.toolProgressProjection.matchesEcho(text), () => this.nextTranscriptTimestamp());
1410
+ this.assistantProjection = new CodexAssistantProjection(params, (event) => this.emitAgentEvent(event), (text) => this.toolProgressProjection.matchesEcho(text), this.transcriptCheckpoint.nextTimestamp, this.transcriptCheckpoint.enqueueCommentary);
1351
1411
  this.reasoningProjection = new CodexReasoningProjection(params, (event) => this.emitAgentEvent(event), options.onNativePlanUpdate);
1352
1412
  }
1353
- nextTranscriptTimestamp() {
1354
- this.lastTranscriptTimestamp = Math.max(Date.now(), this.lastTranscriptTimestamp + 1);
1355
- return this.lastTranscriptTimestamp;
1356
- }
1357
1413
  getCompletedTurnStatus() {
1358
1414
  return this.completedTurn?.status;
1359
1415
  }
1416
+ getActiveMcpToolCall(serverName) {
1417
+ if (this.projectionClosed || this.aborted) return;
1418
+ return this.nativeToolLifecycleProjector.getActiveMcpToolCall(serverName);
1419
+ }
1420
+ recordMcpToolCallReceipt(notification) {
1421
+ if (!this.projectionClosed) this.nativeToolLifecycleProjector.recordMcpToolCallReceipt(notification);
1422
+ }
1360
1423
  buildSteeringTranscriptPrefix() {
1361
1424
  const snapshot = buildCodexSteeringMessagesSnapshot({
1362
1425
  runParams: this.params,
@@ -1376,25 +1439,10 @@ var CodexAppServerEventProjector = class {
1376
1439
  this.pendingSteeringAssistantBoundaryItemId = void 0;
1377
1440
  }
1378
1441
  }
1379
- hasCompletedTerminalAssistantText() {
1380
- return this.assistantProjection.hasCompletedTerminalAssistantText(this.completedItemIds);
1381
- }
1382
- getLatestTerminalAssistantCandidate() {
1383
- return this.assistantProjection.getLatestTerminalAssistantCandidate();
1384
- }
1385
- hasLatestTerminalAssistantCandidateText() {
1386
- return this.assistantProjection.hasLatestTerminalAssistantCandidateText();
1387
- }
1388
- canReleaseLatestTerminalAssistantAfterToolHandoff() {
1389
- return this.assistantProjection.canReleaseLatestTerminalAssistantAfterToolHandoff();
1390
- }
1391
- /** Restores a completed final item after only the enclosing turn timeout fired. */
1392
- recoverCompletedTerminalAssistantAfterTurnWatchTimeout() {
1393
- if (!this.aborted || this.promptError !== "codex app-server attempt timed out" || !this.hasCompletedTerminalAssistantText()) return false;
1394
- this.aborted = false;
1395
- this.promptError = void 0;
1396
- this.promptErrorSource = null;
1397
- return true;
1442
+ /** Fence delayed projections before the turn's final snapshot leaves its owner. */
1443
+ closeProjection() {
1444
+ this.projectionClosed = true;
1445
+ return this.transcriptCheckpoint.flush(true);
1398
1446
  }
1399
1447
  /** Resolves the shared model-order position for a native tool item. */
1400
1448
  recordNativeToolOutcome(item) {
@@ -1410,6 +1458,7 @@ var CodexAppServerEventProjector = class {
1410
1458
  this.nativeToolLifecycleProjector.recordPreToolUseFailure(failure);
1411
1459
  }
1412
1460
  async handleNotification(notification) {
1461
+ if (this.projectionClosed) return;
1413
1462
  const params = isJsonObject(notification.params) ? notification.params : void 0;
1414
1463
  if (!params) return;
1415
1464
  if (notification.method === "hook/started" || notification.method === "hook/completed") {
@@ -1437,9 +1486,11 @@ var CodexAppServerEventProjector = class {
1437
1486
  break;
1438
1487
  case "item/started":
1439
1488
  await this.handleItemStarted(params);
1489
+ await this.transcriptCheckpoint.flush();
1440
1490
  break;
1441
1491
  case "item/completed":
1442
1492
  await this.handleItemCompleted(params);
1493
+ await this.transcriptCheckpoint.flush();
1443
1494
  break;
1444
1495
  case "item/commandExecution/outputDelta":
1445
1496
  this.toolProgressProjection.handleOutputDelta(params, "bash");
@@ -1478,10 +1529,11 @@ var CodexAppServerEventProjector = class {
1478
1529
  await this.handleTurnCompleted(params);
1479
1530
  break;
1480
1531
  case "rawResponse/completed":
1481
- this.responseCompletions.record(params);
1532
+ this.responseCompletions.record(params, this.params.hostCapabilities.reportOutputTokens);
1482
1533
  break;
1483
1534
  case "rawResponseItem/completed":
1484
1535
  await this.handleRawResponseItemCompleted(params);
1536
+ await this.transcriptCheckpoint.flush();
1485
1537
  break;
1486
1538
  case "model/rerouted":
1487
1539
  this.eventProjection.handleModelRerouted(params);
@@ -1492,7 +1544,11 @@ var CodexAppServerEventProjector = class {
1492
1544
  const codexErrorInfo = isJsonObject(params.error) ? params.error.codexErrorInfo : void 0;
1493
1545
  const compactionFailure = codexErrorInfo === "other" && this.isCompacting();
1494
1546
  this.settledTurnFailureFinalizationAllowed = codexErrorInfo === "serverOverloaded" || compactionFailure;
1495
- this.promptError = this.formatCodexErrorMessage(params) ?? "codex app-server error";
1547
+ this.promptError = resolveCodexPromptError({
1548
+ message: readCodexErrorNotificationMessage(params),
1549
+ codexErrorInfo,
1550
+ rateLimits: this.options.readRecentRateLimits?.()
1551
+ }) ?? "codex app-server error";
1496
1552
  this.promptErrorSource = compactionFailure ? "compaction" : "prompt";
1497
1553
  break;
1498
1554
  }
@@ -1507,9 +1563,7 @@ var CodexAppServerEventProjector = class {
1507
1563
  case "model/verification":
1508
1564
  case "turn/moderationMetadata":
1509
1565
  case "model/safetyBuffering/updated": break;
1510
- default:
1511
- this.diagnostics.warnUnknownEvent(notification, params);
1512
- break;
1566
+ default: this.diagnostics.warnUnknownEvent(notification, params);
1513
1567
  }
1514
1568
  }
1515
1569
  buildResult(toolTelemetry, options) {
@@ -1583,16 +1637,7 @@ var CodexAppServerEventProjector = class {
1583
1637
  await runAgentHarnessBeforeCompactionHook({
1584
1638
  sessionFile: this.params.sessionFile,
1585
1639
  messages: await this.toolTranscriptProjection.readMirroredSessionMessages(),
1586
- ctx: {
1587
- runId: this.params.runId,
1588
- agentId: this.params.agentId,
1589
- sessionKey: this.params.sessionKey,
1590
- sessionId: this.params.sessionId,
1591
- workspaceDir: this.params.workspaceDir,
1592
- messageProvider: this.params.messageProvider ?? void 0,
1593
- trigger: this.params.trigger,
1594
- channelId: this.params.messageChannel ?? this.params.messageProvider ?? void 0
1595
- }
1640
+ ctx: this.options.agentHookContext ?? {}
1596
1641
  });
1597
1642
  this.emitAgentEvent({
1598
1643
  stream: "compaction",
@@ -1614,6 +1659,7 @@ var CodexAppServerEventProjector = class {
1614
1659
  phase: "start",
1615
1660
  item
1616
1661
  });
1662
+ if (this.projectionClosed) return;
1617
1663
  this.toolTranscriptProjection.recordNativeToolCall(item);
1618
1664
  this.toolProgressProjection.emitToolResultSummary(item);
1619
1665
  this.emitAgentEvent({
@@ -1638,27 +1684,22 @@ var CodexAppServerEventProjector = class {
1638
1684
  if (!this.asyncDeliveryProjection.allows(item, true)) return;
1639
1685
  const asyncMessage = this.assistantProjection.recordItemCompleted(item, itemId, this.activeItemIds);
1640
1686
  if (asyncMessage) await this.asyncDeliveryProjection.deliver(asyncMessage);
1687
+ if (this.projectionClosed) return;
1641
1688
  this.reasoningProjection.recordItem(item);
1642
1689
  await this.generatedMediaProjection.recordNative(item);
1690
+ if (this.projectionClosed) return;
1643
1691
  if (item?.type === "contextCompaction" && itemId) {
1644
1692
  this.activeCompactionItemIds.delete(itemId);
1645
1693
  this.completedCompactionCount += 1;
1646
1694
  await this.options.onContextCompacted?.();
1695
+ if (this.projectionClosed) return;
1647
1696
  await runAgentHarnessAfterCompactionHook({
1648
1697
  sessionFile: this.params.sessionFile,
1649
1698
  messages: await this.toolTranscriptProjection.readMirroredSessionMessages(),
1650
1699
  compactedCount: -1,
1651
- ctx: {
1652
- runId: this.params.runId,
1653
- agentId: this.params.agentId,
1654
- sessionKey: this.params.sessionKey,
1655
- sessionId: this.params.sessionId,
1656
- workspaceDir: this.params.workspaceDir,
1657
- messageProvider: this.params.messageProvider ?? void 0,
1658
- trigger: this.params.trigger,
1659
- channelId: this.params.messageChannel ?? this.params.messageProvider ?? void 0
1660
- }
1700
+ ctx: this.options.agentHookContext ?? {}
1661
1701
  });
1702
+ if (this.projectionClosed) return;
1662
1703
  await persistCodexContextCompactionActivity({
1663
1704
  sessionTarget: this.params.sessionTarget,
1664
1705
  config: this.params.config,
@@ -1667,7 +1708,7 @@ var CodexAppServerEventProjector = class {
1667
1708
  threadId: this.threadId,
1668
1709
  turnId: this.turnId,
1669
1710
  itemId,
1670
- timestamp: this.nextTranscriptTimestamp()
1711
+ timestamp: this.transcriptCheckpoint.nextTimestamp()
1671
1712
  });
1672
1713
  this.eventProjection.emitCompactionEnd(itemId, true);
1673
1714
  }
@@ -1681,8 +1722,10 @@ var CodexAppServerEventProjector = class {
1681
1722
  phase: "result",
1682
1723
  item
1683
1724
  });
1725
+ if (this.projectionClosed) return;
1684
1726
  this.toolTranscriptProjection.recordNativeToolCall(item);
1685
1727
  await this.toolTranscriptProjection.recordNativeToolResultWithDetails(item);
1728
+ if (this.projectionClosed) return;
1686
1729
  this.toolProgressProjection.emitToolResultSummary(item);
1687
1730
  this.toolProgressProjection.emitToolResultOutput(item);
1688
1731
  this.emitAgentEvent({
@@ -1702,12 +1745,11 @@ var CodexAppServerEventProjector = class {
1702
1745
  this.settledTurnFailureFinalizationAllowed = turn.status === "failed" && (turn.error?.codexErrorInfo === "serverOverloaded" || compactionFailure);
1703
1746
  if (turn.status !== "completed") this.responseCompletions.clear();
1704
1747
  if (turn.status === "failed") {
1705
- const usageLimitMessage = formatCodexUsageLimitErrorMessage({
1748
+ this.promptError = resolveCodexPromptError({
1706
1749
  message: turn.error?.message,
1707
1750
  codexErrorInfo: turn.error?.codexErrorInfo,
1708
1751
  rateLimits: this.options.readRecentRateLimits?.()
1709
- });
1710
- this.promptError = usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : turn.error?.message ?? "codex app-server turn failed";
1752
+ }) ?? "codex app-server turn failed";
1711
1753
  this.promptErrorSource = compactionFailure ? "compaction" : "prompt";
1712
1754
  }
1713
1755
  if (compactionFailure) {
@@ -1720,39 +1762,40 @@ var CodexAppServerEventProjector = class {
1720
1762
  }
1721
1763
  const turnItems = turn.items ?? [];
1722
1764
  const unsettledAsyncDeliveries = this.asyncDeliveryProjection.pending();
1723
- for (let index = turnItems.length - 1; index >= 0; index -= 1) {
1724
- const item = turnItems[index];
1725
- if (!item || !this.isCurrentTurnSnapshotItem(item)) continue;
1726
- if (item?.type === "dynamicToolCall") break;
1727
- if (shouldClearTerminalPresentationForNativeItem(item)) {
1728
- this.clearTerminalPresentationForNativeItem(item);
1729
- break;
1730
- }
1731
- }
1765
+ const lastToolItem = turnItems.findLast((item) => matchesCodexSnapshotTurn(item, this.turnId) && (item.type === "dynamicToolCall" || shouldClearTerminalPresentationForNativeItem(item)));
1766
+ if (lastToolItem?.type !== "dynamicToolCall") this.clearTerminalPresentationForNativeItem(lastToolItem);
1732
1767
  for (const item of turnItems) {
1733
1768
  if (!this.asyncDeliveryProjection.allows(item)) continue;
1734
1769
  this.diagnostics.warnUnknownItemStatus(item);
1735
1770
  const asyncMessage = this.assistantProjection.recordSnapshotItem(item);
1736
1771
  if (asyncMessage) await this.asyncDeliveryProjection.deliver(asyncMessage);
1772
+ if (this.projectionClosed) return;
1737
1773
  this.reasoningProjection.recordItem(item);
1738
1774
  await this.generatedMediaProjection.recordNative(item);
1775
+ if (this.projectionClosed) return;
1739
1776
  this.toolProgressProjection.recordToolMeta(item);
1740
1777
  this.toolProgressProjection.rememberCommandAggregateOutputEcho(item);
1741
1778
  await this.emitSnapshotOnlyNativeToolProgress(item);
1779
+ if (this.projectionClosed) return;
1742
1780
  this.toolTranscriptProjection.recordNativeToolCall(item);
1743
1781
  await this.toolTranscriptProjection.recordNativeToolResultWithDetails(item);
1782
+ if (this.projectionClosed) return;
1744
1783
  this.toolTranscriptProjection.emitAfterToolCallObservation(item);
1745
1784
  this.toolProgressProjection.emitToolResultSummary(item);
1746
1785
  this.toolProgressProjection.emitToolResultOutput(item);
1747
1786
  }
1748
1787
  this.toolProgressProjection.approvalTimeoutKinds.clear();
1749
- for (const delivery of unsettledAsyncDeliveries) await this.asyncDeliveryProjection.deliver(delivery);
1788
+ for (const delivery of unsettledAsyncDeliveries) {
1789
+ if (this.projectionClosed) return;
1790
+ await this.asyncDeliveryProjection.deliver(delivery);
1791
+ }
1792
+ if (this.projectionClosed) return;
1750
1793
  this.assistantProjection.finalizeAnswerCandidate(turn);
1751
1794
  this.activeCompactionItemIds.clear();
1752
1795
  await this.reasoningProjection.maybeEndReasoning();
1753
1796
  }
1754
1797
  async emitSnapshotOnlyNativeToolProgress(item) {
1755
- if (!shouldSynthesizeToolProgressForItem(item) || !this.isCurrentTurnSnapshotItem(item) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
1798
+ if (!shouldSynthesizeToolProgressForItem(item) || !matchesCodexSnapshotTurn(item, this.turnId) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
1756
1799
  if (!this.activeItemIds.has(item.id)) {
1757
1800
  this.eventProjection.emitStandardItemEvent({
1758
1801
  phase: "start",
@@ -1763,6 +1806,7 @@ var CodexAppServerEventProjector = class {
1763
1806
  item
1764
1807
  });
1765
1808
  }
1809
+ if (this.projectionClosed) return;
1766
1810
  this.activeItemIds.delete(item.id);
1767
1811
  this.eventProjection.emitStandardItemEvent({
1768
1812
  phase: "end",
@@ -1774,10 +1818,6 @@ var CodexAppServerEventProjector = class {
1774
1818
  });
1775
1819
  this.completedItemIds.add(item.id);
1776
1820
  }
1777
- isCurrentTurnSnapshotItem(item) {
1778
- const itemTurnId = readItemString(item, "turnId");
1779
- return itemTurnId === void 0 || itemTurnId === this.turnId;
1780
- }
1781
1821
  async handleRawResponseItemCompleted(params) {
1782
1822
  const item = isJsonObject(params.item) ? params.item : void 0;
1783
1823
  if (!item) return;
@@ -1798,17 +1838,8 @@ var CodexAppServerEventProjector = class {
1798
1838
  presentationOnly: true
1799
1839
  });
1800
1840
  }
1801
- formatCodexErrorMessage(params) {
1802
- const error = isJsonObject(params.error) ? params.error : void 0;
1803
- const usageLimitMessage = formatCodexUsageLimitErrorMessage({
1804
- message: error ? readStringField(error, "message") : void 0,
1805
- codexErrorInfo: error?.codexErrorInfo,
1806
- rateLimits: this.options.readRecentRateLimits?.()
1807
- });
1808
- return usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : readCodexErrorNotificationMessage(params);
1809
- }
1810
1841
  emitAgentEvent(event) {
1811
- emitCodexAgentEvent(this.params, event);
1842
+ if (!this.projectionClosed) emitCodexAgentEvent(this.params, event);
1812
1843
  }
1813
1844
  isHookNotificationForCurrentThread(params) {
1814
1845
  const threadId = readStringField(params, "threadId");
@@ -1817,23 +1848,11 @@ var CodexAppServerEventProjector = class {
1817
1848
  }
1818
1849
  };
1819
1850
  //#endregion
1820
- //#region extensions/codex/src/app-server/mcp-tool-metadata.ts
1821
- /** Hosted app ownership is authoritative only on metadata supplied by Codex. */
1822
- function readCodexMcpToolConnectorId(tool) {
1823
- const metadata = asOptionalRecord(asOptionalRecord(tool)?.["_meta"]);
1824
- return normalizeOptionalString(metadata?.connector_id) ?? normalizeOptionalString(metadata?.connectorId);
1825
- }
1826
- /** Preserve MCP App visibility so model-only tools cannot become widget authority. */
1827
- function readCodexMcpToolUiVisibility(tool) {
1828
- const visibility = asOptionalRecord(asOptionalRecord(asOptionalRecord(tool)?.["_meta"])?.ui)?.visibility;
1829
- if (!Array.isArray(visibility)) return;
1830
- return [...new Set(visibility.filter((value) => value === "app" || value === "model"))].toSorted();
1831
- }
1832
- //#endregion
1833
1851
  //#region extensions/codex/src/app-server/native-mcp-app.ts
1834
- const CODEX_APPS_MCP_SERVER$1 = "codex_apps";
1852
+ const CODEX_APPS_MCP_SERVER = "codex_apps";
1835
1853
  function readMcpAppResourceUri(item) {
1836
- const uri = normalizeOptionalString(asOptionalRecord(item.appContext)?.resourceUri) ?? normalizeOptionalString(item.mcpAppResourceUri);
1854
+ const appContext = asOptionalRecord(item.appContext);
1855
+ const uri = normalizeOptionalString(appContext?.resourceUri) ?? normalizeOptionalString(item.mcpAppResourceUri);
1837
1856
  return uri?.startsWith("ui://") ? uri : void 0;
1838
1857
  }
1839
1858
  function readMcpAppConnectorId(item) {
@@ -1914,7 +1933,7 @@ function createNativeMcpRuntime(params) {
1914
1933
  return { tools: status ? statusTools(status) : [] };
1915
1934
  },
1916
1935
  readResource: async (serverName, uri) => {
1917
- const isCodexAppsServer = serverName === CODEX_APPS_MCP_SERVER$1;
1936
+ const isCodexAppsServer = serverName === CODEX_APPS_MCP_SERVER;
1918
1937
  const response = await params.client.request("mcpServer/resource/read", {
1919
1938
  threadId: params.threadId,
1920
1939
  ...isCodexAppsServer ? { originCallId: params.originCallId } : {},
@@ -1944,7 +1963,7 @@ function createCodexNativeMcpAppResultDetailsPreparer(params) {
1944
1963
  const connectorId = readMcpAppConnectorId(item);
1945
1964
  const toolResult = readMcpToolResult(item);
1946
1965
  if (!serverName || !toolName || !uiResourceUri || !toolResult) return;
1947
- if (serverName === CODEX_APPS_MCP_SERVER$1 && !connectorId) return;
1966
+ if (serverName === CODEX_APPS_MCP_SERVER && !connectorId) return;
1948
1967
  const runtime = createNativeMcpRuntime({
1949
1968
  ...params,
1950
1969
  originCallId: item.id,
@@ -1953,7 +1972,7 @@ function createCodexNativeMcpAppResultDetailsPreparer(params) {
1953
1972
  const tools = (await runtime.listTools?.(serverName))?.tools ?? [];
1954
1973
  const allowedAppToolNames = new Set(tools.filter((tool) => {
1955
1974
  const uiVisibility = readCodexMcpToolUiVisibility(tool);
1956
- return (uiVisibility === void 0 || uiVisibility.includes("app")) && (serverName !== CODEX_APPS_MCP_SERVER$1 || readCodexMcpToolConnectorId(tool) === connectorId);
1975
+ return (uiVisibility === void 0 || uiVisibility.includes("app")) && (serverName !== CODEX_APPS_MCP_SERVER || readCodexMcpToolConnectorId(tool) === connectorId);
1957
1976
  }).map((tool) => tool.name));
1958
1977
  if (!allowedAppToolNames.has(toolName)) return;
1959
1978
  return await prepareHarnessNativeMcpAppPreview({
@@ -1974,9 +1993,9 @@ function createCodexNativeMcpAppResultDetailsPreparer(params) {
1974
1993
  const RESTORED_PLAN_PREAMBLE = "OpenClaw restored the session progress card after context compaction. This is application state, not a new user request. Continue the current task and call progress_card whenever the status meaningfully changes.";
1975
1994
  const PROGRESS_CARD_MAX_STEPS = 50;
1976
1995
  const PROGRESS_CARD_MAX_STEP_UTF8_BYTES = 512;
1977
- const PROGRESS_CARD_MAX_UTF8_BYTES = 8 * 1024;
1978
- const RESTORED_PLAN_MAX_MARKDOWN_BYTES = 2 * 1024;
1979
- const RESTORED_PLAN_MAX_PAYLOAD_BYTES = 32 * 1024;
1996
+ const PROGRESS_CARD_MAX_UTF8_BYTES = 8192;
1997
+ const RESTORED_PLAN_MAX_MARKDOWN_BYTES = 2048;
1998
+ const RESTORED_PLAN_MAX_PAYLOAD_BYTES = 32768;
1980
1999
  const RESTORED_PLAN_TRUNCATION_SUFFIX = "…";
1981
2000
  function canonicalizeNativeProgressCardInput(input) {
1982
2001
  const markdown = input.markdown === void 0 ? void 0 : truncateUtf8(stripInvisibleUnicode(input.markdown), PROGRESS_CARD_MAX_UTF8_BYTES);
@@ -1986,8 +2005,10 @@ function canonicalizeNativeProgressCardInput(input) {
1986
2005
  const text = truncateUtf8(stripInvisibleUnicode(step.step), PROGRESS_CARD_MAX_STEP_UTF8_BYTES);
1987
2006
  if (!text.trim()) continue;
1988
2007
  let status = step.status;
1989
- if (status === "in_progress") if (hasInProgressStep) status = "pending";
1990
- else hasInProgressStep = true;
2008
+ if (status === "in_progress") {
2009
+ if (hasInProgressStep) status = "pending";
2010
+ else hasInProgressStep = true;
2011
+ }
1991
2012
  plan.push({
1992
2013
  step: text,
1993
2014
  status
@@ -2056,7 +2077,8 @@ function serializePlan(plan) {
2056
2077
  }
2057
2078
  function truncateUtf8(value, maxBytes) {
2058
2079
  if (Buffer$1.byteLength(value, "utf8") <= maxBytes) return value;
2059
- return `${truncateUtf8Prefix(value, maxBytes - Buffer$1.byteLength(RESTORED_PLAN_TRUNCATION_SUFFIX, "utf8"))}${RESTORED_PLAN_TRUNCATION_SUFFIX}`;
2080
+ const suffixBytes = Buffer$1.byteLength(RESTORED_PLAN_TRUNCATION_SUFFIX, "utf8");
2081
+ return `${truncateUtf8Prefix(value, maxBytes - suffixBytes)}${RESTORED_PLAN_TRUNCATION_SUFFIX}`;
2060
2082
  }
2061
2083
  function readPlanSteps(value) {
2062
2084
  if (!Array.isArray(value)) return [];
@@ -2076,7 +2098,7 @@ function isPlanStatus(value) {
2076
2098
  //#endregion
2077
2099
  //#region extensions/codex/src/app-server/user-input-bridge.ts
2078
2100
  /** Owns per-turn Codex request_user_input and ordinary MCP elicitation lifecycles. */
2079
- const DEFAULT_USER_INPUT_TIMEOUT_MS = 15 * 6e4;
2101
+ const DEFAULT_USER_INPUT_TIMEOUT_MS = 9e5;
2080
2102
  const NONBLOCKING_USER_INPUT_TIMEOUT_MS = 12e4;
2081
2103
  const MAX_USER_INPUT_QUESTIONS = 3;
2082
2104
  const MAX_USER_INPUT_OPTIONS = 4;
@@ -2200,7 +2222,10 @@ function createCodexUserInputBridge(params) {
2200
2222
  failureValue: declineElicitation("OpenClaw could not handle this elicitation."),
2201
2223
  run: async (signal) => {
2202
2224
  const result = await execute(compiled.input, timeoutMs, signal);
2203
- if (result.status === "answered") return createCodexElicitationResponse("accept", compiled.input.kind === "ready" && compiled.input.plan.kind === "url" ? null : result.content);
2225
+ if (result.status === "answered") {
2226
+ const content = compiled.input.kind === "ready" && compiled.input.plan.kind === "url" ? null : result.content;
2227
+ return createCodexElicitationResponse("accept", content);
2228
+ }
2204
2229
  if (result.status === "declined") return declineElicitation(result.message);
2205
2230
  if (result.status === "unsupported") return declineElicitation(result.message);
2206
2231
  return cancelValue;
@@ -2314,14 +2339,14 @@ function declineElicitation(message) {
2314
2339
  }
2315
2340
  //#endregion
2316
2341
  //#region extensions/codex/src/app-server/run-attempt-active-turn.ts
2317
- async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notifications, turn) {
2342
+ function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notifications, turn) {
2318
2343
  const { prompt, state: resourceState, projectorRef, trajectoryRecorder, pendingNativePreToolUseFailures } = resources;
2319
2344
  const { context, turnState } = prompt;
2320
- const { runtime, attemptTools } = context;
2345
+ const { runtime, attemptTools, hookContext } = context;
2321
2346
  const { connection } = runtime;
2322
- const { params, runAbortController, terminalState, abortExplicitly, abortFromUpstream, bindingStore, bindingIdentity, sessionAgentId, sandboxSessionKey, contextSessionKey, effectiveCwd } = connection;
2347
+ const { params, runAbortController, terminalState, abortExplicitly, abortFromUpstream, sessionAgentId, contextSessionKey, effectiveCwd } = connection;
2323
2348
  const { dynamicToolParams, compactionPlanState, computerContextEpoch, toolBridge } = attemptTools;
2324
- const { state, userInputBridgeRef, steeringQueueRef, turnWatches, completeTurn, interruptTurn } = turnRuntime;
2349
+ const { state, completion, userInputBridgeRef, steeringQueueRef, deadlines, noteProgress, completeTurn, interruptTurn } = turnRuntime;
2325
2350
  const { emitExecutionPhaseOnce, emitLifecycleStart, maybeAnnounceFastModeAutoOff } = lifecycle;
2326
2351
  const { enqueueNotification } = notifications;
2327
2352
  const activeTurnId = turn.turn.id;
@@ -2359,6 +2384,7 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2359
2384
  return dynamicToolParams.onAgentEvent?.(event);
2360
2385
  }
2361
2386
  }, resourceState.thread.threadId, activeTurnId, {
2387
+ agentHookContext: hookContext,
2362
2388
  initialContextTokens: connection.mutable.startupContextTokens,
2363
2389
  nativePostToolUseRelayEnabled: resourceState.nativeHookRelay?.allowedEvents.includes("post_tool_use") === true && resourceState.nativeHookRelay.shouldRelayEvent("post_tool_use"),
2364
2390
  asyncUserMessageAllowed: params.disableTools !== true && (params.toolsAllow === void 0 || toolBridge.availableTools.some((tool) => tool.name === "message")),
@@ -2420,59 +2446,111 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2420
2446
  }
2421
2447
  }
2422
2448
  });
2423
- if (isTerminalTurnStatus(turn.turn.status)) state.terminalTurnNotificationQueued = true;
2449
+ if (isTerminalTurnStatus(turn.turn.status)) {
2450
+ state.terminalTurnNotificationQueued = true;
2451
+ deadlines.beginSettlement(Date.now());
2452
+ }
2424
2453
  emitLifecycleStart();
2425
2454
  const activeProjector = projectorRef.current;
2426
- turnWatches.armTerminalIdleWatch();
2427
- turnWatches.touchActivity("turn:start", { arm: true });
2428
- turnWatches.armAttemptIdleWatch();
2429
- turnWatches.touchActivity("turn:start", { attemptProgress: true });
2455
+ noteProgress("turn:start");
2456
+ const abortListener = () => {
2457
+ state.abortCleanup = interruptTurn(activeTurnId).then(async (confirmed) => {
2458
+ if (!terminalState.explicitCancellationObserved && !state.permissionChangeRestart && !state.timeout) return;
2459
+ if (!confirmed) throw new Error(state.permissionChangeRestart ? "Permission change could not confirm the previous Codex turn stopped." : "Codex cancellation could not confirm the turn stopped; background terminals may still be running.");
2460
+ await terminateCodexBackgroundTerminals(resourceState.client, resourceState.thread.threadId);
2461
+ if (state.permissionChangeRestart) state.permissionChangeRestart = "confirmed";
2462
+ });
2463
+ state.abortCleanup.then(completeTurn, (error) => {
2464
+ embeddedAgentLog.warn("codex app-server cancellation cleanup failed", { error });
2465
+ completeTurn();
2466
+ });
2467
+ };
2468
+ runAbortController.signal.addEventListener("abort", abortListener, { once: true });
2469
+ if (runAbortController.signal.aborted) abortListener();
2430
2470
  for (const failure of pendingNativePreToolUseFailures.splice(0)) activeProjector.recordNativeToolPreToolUseFailure(failure);
2431
2471
  const notifyUserMessagePersisted = createCodexAppServerUserMessagePersistenceNotifier(params);
2432
2472
  const promptMirrorPromise = mirrorPromptAtTurnStartBestEffort({
2433
2473
  params,
2434
2474
  agentId: sessionAgentId,
2435
2475
  notifyUserMessagePersisted,
2436
- sessionKey: sandboxSessionKey,
2476
+ sessionKey: contextSessionKey,
2437
2477
  cwd: effectiveCwd,
2438
2478
  threadId: resourceState.thread.threadId,
2439
2479
  turnId: activeTurnId,
2440
2480
  upstreamUserText: turnState.codexTurnPromptText
2441
2481
  });
2442
- await promptMirrorPromise;
2443
- if (resourceState.turnRoute) try {
2444
- await resourceState.turnRoute.bindTurn(activeTurnId);
2445
- } catch (error) {
2446
- if (!state.terminalTurnNotificationQueued) throw error;
2447
- await resourceState.turnRoute.drain();
2448
- if (!state.completed) {
2449
- turnWatches.clearAllTimers();
2450
- throw error;
2451
- }
2452
- }
2453
- if (!state.completed && isTerminalTurnStatus(turn.turn.status)) {
2454
- if (!isJsonObject(turn.turn)) throw new Error("Codex turn completion payload is not a JSON object");
2455
- await enqueueNotification({
2456
- method: "turn/completed",
2457
- params: {
2458
- threadId: resourceState.thread.threadId,
2459
- turnId: activeTurnId,
2460
- turn: turn.turn
2482
+ const bindProjection = async () => {
2483
+ state.activeLocalProjections += 1;
2484
+ try {
2485
+ await Promise.race([promptMirrorPromise, completion]);
2486
+ if (state.completed) return;
2487
+ if (resourceState.turnRoute) try {
2488
+ await Promise.race([resourceState.turnRoute.bindTurn(activeTurnId), completion]);
2489
+ } catch (error) {
2490
+ if (!state.terminalTurnNotificationQueued && !runAbortController.signal.aborted) throw error;
2491
+ await Promise.race([resourceState.turnRoute.drain(), completion]);
2492
+ if (!state.completed) throw error;
2461
2493
  }
2462
- }, {
2463
- threadId: resourceState.thread.threadId,
2464
- turnId: activeTurnId
2465
- });
2466
- }
2494
+ if (!state.completed && isTerminalTurnStatus(turn.turn.status)) {
2495
+ if (!isJsonObject(turn.turn)) throw new Error("Codex turn completion payload is not a JSON object");
2496
+ await enqueueNotification({
2497
+ method: "turn/completed",
2498
+ params: {
2499
+ threadId: resourceState.thread.threadId,
2500
+ turnId: activeTurnId,
2501
+ turn: turn.turn
2502
+ }
2503
+ }, {
2504
+ threadId: resourceState.thread.threadId,
2505
+ turnId: activeTurnId
2506
+ });
2507
+ }
2508
+ } finally {
2509
+ state.activeLocalProjections -= 1;
2510
+ }
2511
+ };
2512
+ const assertSteeringActive = () => {
2513
+ params.hostCapabilities.assertActive();
2514
+ runAbortController.signal.throwIfAborted();
2515
+ if (state.completed || state.terminalTurnNotificationQueued) throw new Error("codex app-server turn is no longer accepting steering");
2516
+ };
2517
+ const workspaceOnly = resolveAttemptFsWorkspaceOnly({
2518
+ config: params.config,
2519
+ sessionAgentId
2520
+ });
2521
+ const imageContext = {
2522
+ workspaceDir: connection.effectiveWorkspace,
2523
+ model: params.model,
2524
+ config: params.config,
2525
+ workspaceOnly,
2526
+ localRoots: workspaceOnly ? void 0 : getAgentScopedMediaLocalRoots(params.config ?? {}, sessionAgentId),
2527
+ sandbox: connection.sandbox?.enabled && connection.sandbox.fsBridge ? {
2528
+ root: connection.sandbox.workspaceDir,
2529
+ bridge: connection.sandbox.fsBridge
2530
+ } : void 0
2531
+ };
2467
2532
  const activeSteeringQueue = createCodexSteeringQueue({
2468
2533
  client: resourceState.client,
2469
2534
  threadId: resourceState.thread.threadId,
2470
2535
  turnId: activeTurnId,
2471
2536
  requestTimeoutMs: connection.appServer.requestTimeoutMs,
2472
2537
  signal: runAbortController.signal,
2538
+ assertActive: assertSteeringActive,
2539
+ prepareMessage: async (text, options) => {
2540
+ const result = await detectAndLoadAgentHarnessPromptImages({
2541
+ ...imageContext,
2542
+ prompt: text,
2543
+ existingImages: options.images,
2544
+ imageOrder: options.imageOrder,
2545
+ media: options.media,
2546
+ userTurnTranscriptRecorder: options.userTurnTranscriptRecorder
2547
+ });
2548
+ if (result.failedMediaCount) throw new Error(`failed to hydrate ${result.failedMediaCount} structured image attachment(s) for Codex steering`);
2549
+ return buildCodexUserInput(text, result.images);
2550
+ },
2473
2551
  beforeConfirmConsumed: async (items) => {
2474
- const inboundItems = items.filter((item) => item.isInboundUserMessage === true);
2475
- if (inboundItems.length === 0) return;
2552
+ const transcriptItems = items.filter((item) => item.isInboundUserMessage === true || item.userTurnTranscriptRecorder !== void 0);
2553
+ if (transcriptItems.length === 0) return;
2476
2554
  await promptMirrorPromise;
2477
2555
  const messages = activeProjector.buildSteeringTranscriptPrefix();
2478
2556
  if (params.sessionTarget && messages.length > 0) {
@@ -2490,7 +2568,7 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2490
2568
  });
2491
2569
  activeProjector.markSteeringTranscriptPersisted();
2492
2570
  }
2493
- for (const item of inboundItems) {
2571
+ for (const item of transcriptItems) {
2494
2572
  const recorder = item.userTurnTranscriptRecorder;
2495
2573
  if (!recorder) continue;
2496
2574
  await recorder.persistApproved();
@@ -2500,7 +2578,8 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2500
2578
  });
2501
2579
  steeringQueueRef.current = activeSteeringQueue;
2502
2580
  const claimPendingUserInputAnswer = async (text, optionsLocal) => {
2503
- if (optionsLocal?.isInboundUserMessage !== true || optionsLocal.images?.length) return false;
2581
+ if (optionsLocal?.isInboundUserMessage !== true || hasPromptImageInput(optionsLocal)) return false;
2582
+ assertSteeringActive();
2504
2583
  return await claimPendingAgentQuestionAnswer({
2505
2584
  sessionKey: params.sessionKey ?? params.sessionId,
2506
2585
  text,
@@ -2514,14 +2593,17 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2514
2593
  resolvedBy
2515
2594
  });
2516
2595
  const queueMessage = async (text, optionsLocal) => {
2517
- const isInboundUserMessage = optionsLocal?.isInboundUserMessage === true;
2518
2596
  if (await claimPendingUserInputAnswer(text, optionsLocal)) {
2519
2597
  optionsLocal?.onQueueAccepted?.(true);
2520
2598
  return;
2521
- } else if (isInboundUserMessage && optionsLocal?.images?.length) try {
2522
- await cancelPendingUserInput("image-reply");
2523
- } catch (error) {
2524
- embeddedAgentLog.warn("failed to cancel codex gateway question before image steering", { error });
2599
+ }
2600
+ if (optionsLocal?.isInboundUserMessage === true && hasPromptImageInput(optionsLocal)) {
2601
+ assertSteeringActive();
2602
+ try {
2603
+ await cancelPendingUserInput("image-reply");
2604
+ } catch (error) {
2605
+ embeddedAgentLog.warn("failed to cancel codex gateway question before image steering", { error });
2606
+ }
2525
2607
  }
2526
2608
  try {
2527
2609
  await activeSteeringQueue.queue(text, optionsLocal);
@@ -2536,21 +2618,32 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2536
2618
  const handle = {
2537
2619
  kind: "embedded",
2538
2620
  runId: params.runId,
2621
+ startedAtMs: params.startedAtMs,
2539
2622
  toolAuthorityFingerprint: params.toolAuthorityFingerprint,
2623
+ permissionChangeOwner: params.permissionChange?.owner,
2624
+ applyPermissionMode: async (mode, revokeApprovals) => {
2625
+ if (!params.permissionChange || terminalState.terminalOutcomeFrozen || params.abortSignal?.aborted || !state.permissionChangeRestart && (state.completed || runAbortController.signal.aborted)) return false;
2626
+ const applied = params.permissionChange.request(mode);
2627
+ state.permissionChangeRestart ??= "requested";
2628
+ runAbortController.abort("permission-change");
2629
+ revokeApprovals();
2630
+ return await applied;
2631
+ },
2540
2632
  claimPendingUserInputAnswer,
2541
2633
  cancelPendingUserInput,
2542
2634
  queueMessage,
2543
2635
  messageInjection: {
2544
- isAvailable: () => !state.completed && !state.terminalTurnNotificationQueued && !state.timedOut && !runAbortController.signal.aborted,
2636
+ isAvailable: () => !state.completed && !state.terminalTurnNotificationQueued && !runAbortController.signal.aborted,
2545
2637
  queueMessage
2546
2638
  },
2547
2639
  isStreaming: () => !state.completed && !runAbortController.signal.aborted,
2548
2640
  isAborted: () => runAbortController.signal.aborted,
2549
- isStopped: () => state.completed || state.timedOut || runAbortController.signal.aborted,
2641
+ isStopped: () => state.completed || runAbortController.signal.aborted,
2642
+ ownsLiveness: () => deadlines.ownsExecutionWait() && resourceState.turnRoute?.signal.aborted === false && !state.completed && !state.terminalTurnNotificationQueued && state.activeAppServerTurnRequests === 0 && state.activeLocalProjections === 0,
2550
2643
  isAbortable: () => !terminalState.terminalOutcomeFrozen || terminalState.sharedAbortAllowedAfterTerminalOutcome,
2551
2644
  isCompacting: () => projectorRef.current?.isCompacting() ?? false,
2552
2645
  supportsTranscriptCommitWait: true,
2553
- supportsQueueMessageImages: true,
2646
+ supportsQueueMessageImages: params.model.input.includes("image"),
2554
2647
  sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
2555
2648
  taskSuggestionDeliveryMode: params.taskSuggestionDeliveryMode,
2556
2649
  cancel: () => abortExplicitly("cancelled"),
@@ -2563,26 +2656,10 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2563
2656
  terminalState.terminalOutcomeFrozen = true;
2564
2657
  params.abortSignal?.removeEventListener("abort", abortFromUpstream);
2565
2658
  };
2566
- const abortListener = () => {
2567
- if (state.timedOut) {
2568
- (async () => {
2569
- if (resourceState.thread.connectionScope !== "supervision") await bindingStore.mutate(bindingIdentity, {
2570
- kind: "clear",
2571
- threadId: resourceState.thread.threadId
2572
- });
2573
- await retireCodexAppServerClientAfterTimedOutTurn(resourceState.client, {
2574
- threadId: resourceState.thread.threadId,
2575
- turnId: activeTurnId,
2576
- reason: String(runAbortController.signal.reason ?? "timeout"),
2577
- suspectPhysicalClient: state.turnWatchTimeoutKind === "terminal"
2578
- });
2579
- })().finally(completeTurn);
2580
- return;
2581
- }
2582
- interruptTurn(activeTurnId).finally(completeTurn);
2583
- };
2584
- runAbortController.signal.addEventListener("abort", abortListener, { once: true });
2585
- if (runAbortController.signal.aborted) abortListener();
2659
+ if (!runAbortController.signal.aborted && params.permissionChange && !params.permissionChange.applied()) {
2660
+ state.permissionChangeRestart = "requested";
2661
+ runAbortController.abort("permission-change");
2662
+ }
2586
2663
  return {
2587
2664
  activeTurnId,
2588
2665
  activeProjector,
@@ -2590,7 +2667,8 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
2590
2667
  handle,
2591
2668
  freezeRunTerminalOutcome,
2592
2669
  notifyUserMessagePersisted,
2593
- abortListener
2670
+ abortListener,
2671
+ ready: bindProjection()
2594
2672
  };
2595
2673
  }
2596
2674
  //#endregion
@@ -2599,11 +2677,16 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2599
2677
  const { prompt, state: resourceState, trajectoryRecorder, releaseCurrentRoute, releaseSharedClientLeaseAndRetireOneShotClient, releaseSandboxExecEnvironment, runCleanupStep } = resources;
2600
2678
  const { connection } = prompt.context.runtime;
2601
2679
  const { params, options, runAbortController, terminalState, bindingStore, bindingIdentity } = connection;
2602
- const { state, steeringQueueRef, userInputBridgeRef, turnWatches } = turnRuntime;
2680
+ const { state, steeringQueueRef, userInputBridgeRef, deadlines } = turnRuntime;
2603
2681
  const { maybeEmitFastModeAutoResetBestEffort, emitLifecycleTerminal, buildLifecycleTerminalMeta } = lifecycle;
2604
2682
  const { codexModelCallDiagnostics } = requestRuntime;
2605
2683
  const { activeTurnId, abortListener, handle, freezeRunTerminalOutcome } = activeTurn;
2606
2684
  prompt.context.attemptTools.scheduledAppAuthoritySourceRef.current = void 0;
2685
+ freezeRunTerminalOutcome();
2686
+ const projectionClose = state.projectionClosed ? void 0 : activeTurn.activeProjector.closeProjection();
2687
+ state.projectionClosed = true;
2688
+ const checkpointCleanup = projectionClose ? runCleanupStep("codex-transcript-checkpoint", () => projectionClose) : void 0;
2689
+ await state.abortCleanup.catch(() => void 0);
2607
2690
  try {
2608
2691
  steeringQueueRef.current?.cancel();
2609
2692
  if (params.isFinalFallbackAttempt !== false) await maybeEmitFastModeAutoResetBestEffort();
@@ -2613,36 +2696,32 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2613
2696
  error: "codex app-server run completed without lifecycle terminal event",
2614
2697
  ...buildLifecycleTerminalMeta({
2615
2698
  aborted: runAbortController.signal.aborted && !state.clientClosedAbort,
2616
- timedOut: state.timedOut
2699
+ timedOut: state.timeout !== void 0
2617
2700
  })
2618
2701
  });
2619
2702
  if (trajectoryRecorder && !resourceState.trajectoryEndRecorded) trajectoryRecorder.recordEvent("session.ended", {
2620
- status: state.timedOut || runAbortController.signal.aborted && !state.clientClosedAbort ? "interrupted" : "cleanup",
2703
+ status: state.timeout || runAbortController.signal.aborted && !state.clientClosedAbort ? "interrupted" : "cleanup",
2621
2704
  threadId: resourceState.thread.threadId,
2622
2705
  turnId: activeTurnId,
2623
- timedOut: state.timedOut,
2706
+ timedOut: state.timeout !== void 0,
2624
2707
  aborted: runAbortController.signal.aborted && !state.clientClosedAbort
2625
2708
  });
2626
2709
  await runCleanupStep("codex-trajectory-flush", () => trajectoryRecorder?.flush());
2627
- const retainLiveIncognitoThread = terminalState.turnSucceeded && isIncognitoSessionKey(params.sessionKey);
2628
- 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 () => {
2710
+ const retainLiveIncognitoThread = (terminalState.turnSucceeded || state.permissionChangeRestart === "confirmed" && !params.abortSignal?.aborted) && isIncognitoSessionKey(params.sessionKey);
2711
+ const retainLiveThread = ((retainLiveIncognitoThread && resourceState.thread.liveThreadEphemeralPolicy !== void 0 || terminalState.turnSucceeded && !isIncognitoSessionKey(params.sessionKey) && params.cleanupBundleMcpOnRunEnd !== true && resourceState.thread.liveThreadConfigFingerprint !== void 0 && resourceState.thread.preserveNativeModel !== true && resourceState.thread.connectionScope !== "supervision" && !resourceState.thread.ringZeroConfigFingerprint) && resourceState.thread.clientId === resolveCodexAppServerClientInstanceId(resourceState.client) ? (await bindingStore.read(bindingIdentity))?.threadId === resourceState.thread.threadId && await bindingStore.withLease(bindingIdentity, async () => {
2629
2712
  if ((await bindingStore.read(bindingIdentity))?.threadId !== resourceState.thread.threadId) return false;
2630
- return await retainCodexAppServerLiveThread(resourceState.client, resourceState.thread.threadId, resourceState.thread.liveThreadOwnership?.release ?? (async (threadId) => {
2631
- if (!await unsubscribeCodexThreadBestEffort(resourceState.client, {
2632
- threadId,
2633
- timeoutMs: 5e3
2634
- })) {
2635
- await closeCodexStartupClientBestEffort(resourceState.client);
2636
- throw new CodexAppServerUnsafeSubscriptionError(`Codex retained thread subscription could not be released: ${threadId}`);
2637
- }
2638
- }), resourceState.thread.liveThreadConfigFingerprint, connection.mutable.pluginAppServer.serviceTier);
2639
- }) : false;
2640
- const retainLiveThread = retainLiveIncognitoThread || retainedPersistentThread;
2641
- const bindingReleased = isIncognitoSessionKey(params.sessionKey) && !retainLiveIncognitoThread ? await bindingStore.mutate(bindingIdentity, {
2713
+ return await retainCodexAppServerBindingSubscription(resourceState.client, resourceState.thread.threadId, {
2714
+ release: resourceState.thread.liveThreadOwnership?.release,
2715
+ configFingerprint: resourceState.thread.liveThreadConfigFingerprint,
2716
+ serviceTier: connection.mutable.pluginAppServer.serviceTier,
2717
+ ephemeralPolicy: resourceState.thread.liveThreadEphemeralPolicy
2718
+ });
2719
+ }) : false) || retainLiveIncognitoThread && resourceState.thread.liveThreadEphemeralPolicy === void 0;
2720
+ const bindingReleased = isIncognitoSessionKey(params.sessionKey) && !retainLiveThread ? await bindingStore.mutate(bindingIdentity, {
2642
2721
  kind: "clear",
2643
2722
  threadId: resourceState.thread.threadId
2644
2723
  }) : true;
2645
- if (!state.timedOut && !retainLiveThread) {
2724
+ if (!retainLiveThread) {
2646
2725
  if (bindingReleased) {
2647
2726
  if (!await unsubscribeCodexThreadBestEffort(resourceState.client, {
2648
2727
  threadId: resourceState.thread.threadId,
@@ -2652,8 +2731,14 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2652
2731
  }
2653
2732
  } finally {
2654
2733
  await runCleanupStep("codex-user-input-cancel", () => userInputBridgeRef.current?.cancelPending());
2655
- await runCleanupStep("codex-turn-watch-clear", () => turnWatches.clearAllTimers());
2734
+ await runCleanupStep("codex-turn-deadline-clear", () => deadlines.dispose());
2735
+ await runCleanupStep("codex-dynamic-tool-cleanup", async () => {
2736
+ const cleanupReason = terminalState.turnSucceeded ? "completion" : state.timeout ? "timeout" : runAbortController.signal.aborted ? "cancel" : "error";
2737
+ const cleanups = prompt.context.attemptTools.runCleanups.splice(0);
2738
+ await Promise.allSettled(cleanups.map(async (cleanup) => await cleanup(cleanupReason)));
2739
+ });
2656
2740
  await runCleanupStep("codex-route-release", releaseCurrentRoute);
2741
+ await checkpointCleanup;
2657
2742
  await runCleanupStep("codex-shared-client-release", releaseSharedClientLeaseAndRetireOneShotClient);
2658
2743
  const nativeHookRelay = resourceState.nativeHookRelay;
2659
2744
  resourceState.nativeHookRelay = void 0;
@@ -2667,17 +2752,17 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
2667
2752
  });
2668
2753
  await runCleanupStep("codex-sandbox-release", releaseSandboxExecEnvironment);
2669
2754
  await runCleanupStep("codex-scoped-mcp-dispose", () => prompt.context.attemptTools.scopedMcpTools?.dispose());
2670
- await runCleanupStep("codex-scheduled-mcp-dispose", () => prompt.context.attemptTools.scheduledConfiguredMcp?.dispose());
2755
+ await runCleanupStep("codex-configured-mcp-dispose", () => prompt.context.attemptTools.configuredMcp?.dispose());
2671
2756
  await runCleanupStep("codex-abort-listener-remove", () => {
2672
2757
  runAbortController.signal.removeEventListener("abort", abortListener);
2673
2758
  });
2674
2759
  await runCleanupStep("codex-steering-cancel", () => steeringQueueRef.current?.cancel());
2675
- await runCleanupStep("codex-terminal-freeze", freezeRunTerminalOutcome);
2676
2760
  await runCleanupStep("codex-reply-backend-detach", () => params.replyOperation?.detachBackend(handle));
2677
2761
  await runCleanupStep("codex-active-run-clear", () => {
2678
2762
  clearActiveEmbeddedRun(params.sessionId, handle, params.sessionKey, params.sessionFile);
2679
2763
  });
2680
2764
  }
2765
+ await state.abortCleanup;
2681
2766
  }
2682
2767
  //#endregion
2683
2768
  //#region extensions/codex/src/app-server/workspace-dir-cache.ts
@@ -2751,21 +2836,21 @@ const CODEX_APP_SERVER_NATIVE_THREAD_FALLBACK_MAX_TOKENS = 3e5;
2751
2836
  const CODEX_APP_SERVER_NATIVE_THREAD_DEFAULT_RESERVE_TOKENS = 2e4;
2752
2837
  const CODEX_APP_SERVER_NATIVE_THREAD_MIN_PROMPT_BUDGET_TOKENS = 8e3;
2753
2838
  const CODEX_APP_SERVER_NATIVE_THREAD_MIN_PROMPT_BUDGET_RATIO = .5;
2754
- const CODEX_APP_SERVER_ROLLOUT_TAIL_READ_BYTES = 64 * 1024;
2839
+ const CODEX_APP_SERVER_ROLLOUT_TAIL_READ_BYTES = 65536;
2755
2840
  const CODEX_APP_SERVER_BYTE_UNITS = {
2756
2841
  b: 1,
2757
2842
  k: 1024,
2758
2843
  kb: 1024,
2759
2844
  kib: 1024,
2760
- m: 1024 * 1024,
2761
- mb: 1024 * 1024,
2762
- mib: 1024 * 1024,
2763
- g: 1024 * 1024 * 1024,
2764
- gb: 1024 * 1024 * 1024,
2765
- gib: 1024 * 1024 * 1024,
2766
- t: 1024 * 1024 * 1024 * 1024,
2767
- tb: 1024 * 1024 * 1024 * 1024,
2768
- tib: 1024 * 1024 * 1024 * 1024
2845
+ m: 1048576,
2846
+ mb: 1048576,
2847
+ mib: 1048576,
2848
+ g: 1073741824,
2849
+ gb: 1073741824,
2850
+ gib: 1073741824,
2851
+ t: 1099511627776,
2852
+ tb: 1099511627776,
2853
+ tib: 1099511627776
2769
2854
  };
2770
2855
  const codexSessionRecordCache = /* @__PURE__ */ new Map();
2771
2856
  function parseCodexAppServerByteLimit(value) {
@@ -3062,11 +3147,12 @@ async function prepareCodexAttemptConnection({ params, options }) {
3062
3147
  const pluginConfig = readCodexPluginConfig(options.pluginConfig);
3063
3148
  const requirementsToml = readCodexRequirementsToml({});
3064
3149
  const computerUseConfig = resolveCodexComputerUseConfig({ pluginConfig });
3065
- const { sessionAgentId } = resolveSessionAgentIds({
3150
+ const { sessionAgentId } = resolveSessionAgentIdsStrict({
3066
3151
  sessionKey: params.sessionKey,
3067
3152
  config: params.config,
3068
3153
  agentId: params.agentId
3069
3154
  });
3155
+ const policyAgentId = params.sandboxAgentId ?? sessionAgentId;
3070
3156
  preDynamicStartupStages.mark("config");
3071
3157
  const resolvedWorkspace = resolveUserPath(params.workspaceDir);
3072
3158
  await ensureCodexWorkspaceDirOnce(resolvedWorkspace);
@@ -3075,6 +3161,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3075
3161
  const contextSessionKey = params.sessionKey?.trim() || sandboxSessionKey;
3076
3162
  const sandbox = params.sandbox !== void 0 ? params.sandbox : await resolveSandboxContext({
3077
3163
  config: params.config,
3164
+ agentId: params.sandboxAgentId,
3078
3165
  sessionKey: sandboxSessionKey,
3079
3166
  workspaceDir: resolvedWorkspace
3080
3167
  });
@@ -3085,18 +3172,24 @@ async function prepareCodexAttemptConnection({ params, options }) {
3085
3172
  execOverrides: params.execOverrides,
3086
3173
  approvals: params.permissionMode === "full" ? void 0 : loadExecApprovals(),
3087
3174
  config: params.config,
3088
- agentId: sessionAgentId
3175
+ agentId: policyAgentId
3089
3176
  });
3090
3177
  const agentDir = params.agentDir ?? resolveAgentDir$1(params.config ?? {}, sessionAgentId);
3091
3178
  const preparedEnvironment = params.hostCapabilities.preparedEnvironment?.();
3092
3179
  const remoteExec = isCodexRemoteExecPlacementSandbox(sandbox);
3180
+ const assertLocalTargetSupported = (unsupported) => {
3181
+ if (preparedEnvironment?.localProcessEnv && unsupported) throw new Error("This runtime cannot target the diagnosed local installation. Use the saved prompt with a suggested external or manual handoff on this machine.");
3182
+ };
3183
+ assertLocalTargetSupported(sandbox?.enabled === true || remoteExec);
3093
3184
  const preparedShellEnvironment = preparedEnvironment ? {
3094
3185
  ...preparedEnvironment.credentialScrubEnv,
3095
- ...!remoteExec ? preparedEnvironment.localIdentityEnv : void 0
3186
+ ...sandbox?.enabled || remoteExec ? void 0 : preparedEnvironment.localIdentityEnv,
3187
+ ...preparedEnvironment.localProcessEnv
3096
3188
  } : void 0;
3097
3189
  const shellEnvironment = preparedShellEnvironment && Object.keys(preparedShellEnvironment).length > 0 ? preparedShellEnvironment : void 0;
3098
- const disableLoginShell = remoteExec || preparedEnvironment?.managedLocalIdentity === true || preparedEnvironment !== void 0 && Object.keys(preparedEnvironment.credentialScrubEnv).length > 0;
3190
+ const disableLoginShell = remoteExec || preparedEnvironment?.localProcessEnv !== void 0 || preparedEnvironment?.managedLocalIdentity === true || preparedEnvironment !== void 0 && Object.keys(preparedEnvironment.credentialScrubEnv).length > 0;
3099
3191
  const withPreparedProcessEnv = (appServer) => {
3192
+ assertLocalTargetSupported(appServer.start.transport === "websocket" || Boolean(appServer.remoteWorkspaceRoot));
3100
3193
  return shellEnvironment ? {
3101
3194
  ...appServer,
3102
3195
  start: {
@@ -3118,7 +3211,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3118
3211
  preDynamicStartupStages.mark("session-agent");
3119
3212
  let activeContextEngine = isActiveHarnessContextEngine(params.contextEngine) ? params.contextEngine : void 0;
3120
3213
  const isInactiveThreadBootstrapBinding = (binding) => !activeContextEngine && binding?.contextEngine?.projection?.mode === "thread_bootstrap";
3121
- if (bindingIdentity.kind === "session" && bindingIdentity.sessionKey && (params.sessionTarget?.storePath || params.config?.session?.store)) {
3214
+ if (bindingIdentity.kind === "session" && bindingIdentity.sessionKey) {
3122
3215
  const authority = resolveCodexRunSessionBindingAuthority({
3123
3216
  identity: bindingIdentity,
3124
3217
  config: params.config,
@@ -3361,6 +3454,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
3361
3454
  pluginConfig,
3362
3455
  computerUseConfig,
3363
3456
  sessionAgentId,
3457
+ policyAgentId,
3364
3458
  resolvedWorkspace,
3365
3459
  sandboxSessionKey,
3366
3460
  contextSessionKey,
@@ -3378,629 +3472,21 @@ async function prepareCodexAttemptConnection({ params, options }) {
3378
3472
  startupPreparedAuth,
3379
3473
  startupClientAuthProfileId,
3380
3474
  effectiveWorkspace,
3381
- effectiveCwd,
3382
- appServer,
3383
- sessionPermissionPolicy,
3384
- nativeHookRelayEvents,
3385
- runAbortController,
3386
- terminalState,
3387
- abortExplicitly,
3388
- abortFromUpstream,
3389
- resolveReviewerPolicyContext,
3390
- resolveRuntimeOptionsForCurrentBinding,
3391
- mutable,
3392
- initialStartupBindingHadInactiveThreadBootstrap,
3393
- initialInactiveThreadBootstrapBindingForcedFreshStart
3394
- };
3395
- }
3396
- //#endregion
3397
- //#region extensions/codex/src/app-server/attempt-context.ts
3398
- /**
3399
- * Builds Codex app-server prompt context, workspace bootstrap injections,
3400
- * system-prompt reports, and context-engine projection decisions.
3401
- */
3402
- const CODEX_NATIVE_PROJECT_DOC_BASENAMES = /* @__PURE__ */ new Set(["agents.md"]);
3403
- const CODEX_TURN_SCOPED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = /* @__PURE__ */ new Set([
3404
- "identity.md",
3405
- "soul.md",
3406
- "user.md"
3407
- ]);
3408
- const CODEX_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = new Set(CODEX_TURN_SCOPED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES);
3409
- const CODEX_MEMORY_CONTEXT_BASENAME = "memory.md";
3410
- const CODEX_MEMORY_TOOL_NAMES = /* @__PURE__ */ new Set(["memory_search", "memory_get"]);
3411
- const CODEX_BOOTSTRAP_CONTEXT_ORDER = /* @__PURE__ */ new Map([
3412
- ["soul.md", 10],
3413
- ["identity.md", 20],
3414
- ["user.md", 30],
3415
- ["bootstrap.md", 50],
3416
- ["memory.md", 60]
3417
- ]);
3418
- /** Reads mirrored Codex session history for harness hooks. */
3419
- async function readMirroredSessionHistoryMessages(params) {
3420
- const { admission, ...target } = params;
3421
- const messages = await readCodexMirroredSessionHistoryMessages(target, admission);
3422
- if (!messages) embeddedAgentLog.warn("failed to read mirrored session history for codex harness hooks", { sessionFile: params.sessionFile });
3423
- return messages;
3424
- }
3425
- /** Reads a valid thread-bootstrap projection request from context-engine output. */
3426
- function readContextEngineThreadBootstrapProjection(projection) {
3427
- if (projection?.mode !== "thread_bootstrap") return;
3428
- const epoch = projection.epoch?.trim();
3429
- if (!epoch) {
3430
- embeddedAgentLog.warn("context engine requested Codex thread-bootstrap projection without an epoch; using per-turn projection");
3431
- return;
3432
- }
3433
- const fingerprint = projection.fingerprint?.trim();
3434
- return {
3435
- mode: "thread_bootstrap",
3436
- epoch,
3437
- ...fingerprint ? { fingerprint } : {}
3438
- };
3439
- }
3440
- /**
3441
- * Decides whether an existing Codex thread can reuse its context-engine
3442
- * bootstrap projection or must be reprojected.
3443
- */
3444
- function resolveContextEngineBootstrapProjectionDecision(params) {
3445
- const bindingProjection = params.startupBinding?.contextEngine?.projection;
3446
- if (!params.startupBinding?.threadId || !bindingProjection) return {
3447
- project: true,
3448
- reason: !params.startupBinding?.threadId ? "missing-thread-binding" : "missing-projection-binding"
3449
- };
3450
- if (!params.expectedBinding || !isContextEngineBindingCompatible(params.startupBinding.contextEngine, params.expectedBinding)) return {
3451
- project: true,
3452
- reason: "context-engine-binding-mismatch"
3453
- };
3454
- if (!areCodexDynamicToolFingerprintsCompatible({
3455
- previous: params.startupBinding.dynamicToolsFingerprint,
3456
- next: params.dynamicToolsFingerprint,
3457
- nextLegacy: params.legacyDynamicToolsFingerprint
3458
- })) return {
3459
- project: true,
3460
- reason: "dynamic-tools-mismatch"
3461
- };
3462
- return bindingProjection.mode !== "thread_bootstrap" || bindingProjection.epoch !== params.projection.epoch || bindingProjection.fingerprint !== params.projection.fingerprint ? {
3463
- project: true,
3464
- reason: "projection-mismatch"
3465
- } : {
3466
- project: false,
3467
- reason: "matching-thread-bootstrap-binding"
3468
- };
3469
- }
3470
- /**
3471
- * Loads workspace bootstrap files and partitions them into Codex-native prompt,
3472
- * developer-instruction, heartbeat, and memory-tool contexts.
3473
- */
3474
- async function buildCodexWorkspaceBootstrapContext(params) {
3475
- try {
3476
- const inheritsAgentWorkspace = (params.executionWorkspace ?? params.resolvedWorkspace) !== params.resolvedWorkspace;
3477
- const promptWorkspace = inheritsAgentWorkspace ? params.resolvedWorkspace : params.effectiveWorkspace;
3478
- const memoryToolsAvailable = params.memoryToolNames.length > 0 && canRouteCodexWorkspaceMemoryThroughTools({
3479
- config: params.params.config,
3480
- agentId: params.params.agentId ?? params.sessionAgentId,
3481
- workspaceDir: inheritsAgentWorkspace ? params.resolvedWorkspace : params.effectiveWorkspace
3482
- });
3483
- const bootstrapFiles = await resolveBootstrapFilesForRun({
3484
- workspaceDir: params.resolvedWorkspace,
3485
- config: params.params.config,
3486
- sessionKey: params.sessionKey,
3487
- sessionId: params.params.sessionId,
3488
- chatType: params.params.chatType,
3489
- agentId: params.params.agentId ?? params.sessionAgentId,
3490
- warn: (message) => embeddedAgentLog.warn(message),
3491
- contextMode: params.params.bootstrapContextMode,
3492
- runKind: params.params.bootstrapContextRunKind
3493
- });
3494
- const memoryToolRoutedBootstrapFiles = memoryToolsAvailable ? selectCodexWorkspaceMemoryReferenceFiles({
3495
- bootstrapFiles,
3496
- workspaceDir: params.resolvedWorkspace
3497
- }) : [];
3498
- const memoryReferenceFiles = memoryToolRoutedBootstrapFiles.map((file) => remapCodexContextFilePath({
3499
- file: toCodexEmbeddedContextFile(file),
3500
- sourceWorkspaceDir: params.resolvedWorkspace,
3501
- targetWorkspaceDir: promptWorkspace
3502
- }));
3503
- const contextFiles = buildBootstrapContextForFiles(memoryToolsAvailable ? bootstrapFiles.filter((file) => !isCodexWorkspaceRootMemoryBootstrapFile({
3504
- file,
3505
- workspaceDir: params.resolvedWorkspace
3506
- })) : bootstrapFiles, {
3507
- config: params.params.config,
3508
- agentId: params.params.agentId ?? params.sessionAgentId,
3509
- warn: (message) => embeddedAgentLog.warn(message)
3510
- }).map((file) => remapCodexContextFilePath({
3511
- file,
3512
- sourceWorkspaceDir: params.resolvedWorkspace,
3513
- targetWorkspaceDir: promptWorkspace
3514
- }));
3515
- const promptContextFiles = selectCodexWorkspacePromptContextFiles(contextFiles, {
3516
- excludeMemory: memoryToolsAvailable,
3517
- memoryWorkspaceDir: params.effectiveWorkspace
3518
- });
3519
- const injectOpenClawContext = shouldInjectCodexOpenClawPromptContext(params.params);
3520
- const restrictedProjectDocNeedsOpenClawCarrier = params.params.pluginHarnessToolPolicyRestricted === true && !params.params.disableTools && !isMessageOnlyCodexSourceReply(params.params) && params.params.bootstrapContextMode !== "lightweight";
3521
- const threadDeveloperInstructionFiles = injectOpenClawContext && !params.ringZeroActive && (inheritsAgentWorkspace || restrictedProjectDocNeedsOpenClawCarrier) ? selectCodexWorkspaceAgentProjectInstructionFiles(contextFiles, params.resolvedWorkspace) : [];
3522
- const turnScopedDeveloperInstructionFiles = injectOpenClawContext ? selectCodexWorkspaceTurnScopedDeveloperInstructionFiles(contextFiles) : [];
3523
- return {
3524
- bootstrapFiles,
3525
- contextFiles,
3526
- inheritsAgentWorkspace,
3527
- promptContextFiles,
3528
- threadDeveloperInstructionFiles,
3529
- turnScopedDeveloperInstructionFiles,
3530
- memoryReferenceFiles,
3531
- memoryToolRoutedBootstrapFiles,
3532
- memoryToolNames: [...params.memoryToolNames],
3533
- memoryToolRouted: memoryToolsAvailable,
3534
- promptContext: renderCodexWorkspaceBootstrapPromptContext(promptContextFiles),
3535
- threadDeveloperInstructions: renderCodexWorkspaceDeveloperInstructions({
3536
- files: threadDeveloperInstructionFiles,
3537
- header: "## OpenClaw Agent Workspace Instructions",
3538
- preamble: "OpenClaw loaded this bounded snapshot from the configured agent workspace."
3539
- }),
3540
- turnScopedDeveloperInstructions: renderCodexWorkspaceCollaborationDeveloperInstructions(turnScopedDeveloperInstructionFiles),
3541
- memoryCollaborationInstructions: shouldInjectCodexOpenClawPromptContext(params.params) ? await renderCodexWorkspaceMemoryCollaborationInstructions({
3542
- files: memoryReferenceFiles,
3543
- toolNames: params.memoryToolNames,
3544
- memoryToolRouted: memoryToolsAvailable,
3545
- citationsMode: params.params.config?.memory?.citations,
3546
- agentId: params.params.agentId ?? params.sessionAgentId,
3547
- agentSessionKey: params.sessionKey,
3548
- sandboxed: params.sandboxed
3549
- }) : void 0
3550
- };
3551
- } catch (error) {
3552
- embeddedAgentLog.warn("failed to load codex workspace bootstrap instructions", { error });
3553
- return {
3554
- bootstrapFiles: [],
3555
- contextFiles: [],
3556
- inheritsAgentWorkspace: false
3557
- };
3558
- }
3559
- }
3560
- /**
3561
- * Builds the prompt-size, bootstrap-file, skill, and tool-schema accounting
3562
- * report for a Codex run.
3563
- */
3564
- function buildCodexSystemPromptReport(params) {
3565
- const toolEntries = flattenCodexDynamicToolFunctions(params.tools).map(buildCodexToolReportEntry);
3566
- const schemaChars = toolEntries.reduce((sum, tool) => sum + tool.schemaChars, 0);
3567
- const skillsPrompt = params.skillsPrompt.trim();
3568
- const bootstrapMaxChars = readPositiveNumber(params.attempt.config?.agents?.defaults?.bootstrapMaxChars);
3569
- const bootstrapTotalMaxChars = readPositiveNumber(params.attempt.config?.agents?.defaults?.bootstrapTotalMaxChars);
3570
- return {
3571
- source: "run",
3572
- generatedAt: Date.now(),
3573
- sessionId: params.attempt.sessionId,
3574
- sessionKey: params.sessionKey,
3575
- provider: params.attempt.provider,
3576
- model: params.attempt.modelId,
3577
- workspaceDir: params.workspaceDir,
3578
- ...bootstrapMaxChars ? { bootstrapMaxChars } : {},
3579
- ...bootstrapTotalMaxChars ? { bootstrapTotalMaxChars } : {},
3580
- systemPrompt: {
3581
- chars: params.developerInstructions.length,
3582
- projectContextChars: 0,
3583
- nonProjectContextChars: params.developerInstructions.length,
3584
- hash: sha256Text(params.developerInstructions)
3585
- },
3586
- injectedWorkspaceFiles: buildCodexBootstrapInjectionStats({
3587
- bootstrapFiles: params.workspaceBootstrapContext.bootstrapFiles,
3588
- injectedFiles: params.workspaceBootstrapContext.promptContextFiles ?? [],
3589
- developerInstructionFiles: [...params.workspaceBootstrapContext.threadDeveloperInstructionFiles ?? [], ...params.workspaceBootstrapContext.turnScopedDeveloperInstructionFiles ?? []],
3590
- memoryToolRoutedBootstrapFiles: params.workspaceBootstrapContext.memoryToolRoutedBootstrapFiles ?? [],
3591
- memoryToolRouted: params.workspaceBootstrapContext.memoryToolRouted === true
3592
- }),
3593
- skills: {
3594
- promptChars: skillsPrompt.length,
3595
- hash: sha256Text(skillsPrompt),
3596
- entries: buildCodexSkillReportEntries(skillsPrompt)
3597
- },
3598
- tools: {
3599
- listChars: 0,
3600
- schemaChars,
3601
- entries: toolEntries
3602
- }
3603
- };
3604
- }
3605
- function buildCodexSkillReportEntries(skillsPrompt) {
3606
- if (!skillsPrompt) return [];
3607
- return Array.from(skillsPrompt.matchAll(/<skill>[\s\S]*?<\/skill>/gi)).map((match) => match[0] ?? "").map((block) => ({
3608
- name: block.match(/<name>\s*([^<]+?)\s*<\/name>/i)?.[1]?.trim() || "(unknown)",
3609
- blockChars: block.length
3610
- })).filter((entry) => entry.blockChars > 0);
3611
- }
3612
- function buildCodexToolReportEntry(tool) {
3613
- const summary = tool.description.trim();
3614
- if (tool.deferLoading === true) return {
3615
- name: tool.name,
3616
- summaryChars: summary.length,
3617
- summaryHash: sha256Text(summary),
3618
- schemaChars: 0,
3619
- schemaHash: stableJsonHash(null),
3620
- propertiesCount: null
3621
- };
3622
- return {
3623
- name: tool.name,
3624
- summaryChars: summary.length,
3625
- summaryHash: sha256Text(summary),
3626
- ...buildCodexToolSchemaStats(tool.inputSchema)
3627
- };
3628
- }
3629
- function buildCodexToolSchemaStats(schema) {
3630
- const schemaChars = (() => {
3631
- try {
3632
- return JSON.stringify(schema).length;
3633
- } catch {
3634
- return 0;
3635
- }
3636
- })();
3637
- const properties = isJsonObject(schema) && isJsonObject(schema.properties) ? schema.properties : null;
3638
- return {
3639
- schemaChars,
3640
- schemaHash: stableJsonHash(schema),
3641
- propertiesCount: properties ? Object.keys(properties).length : null
3642
- };
3643
- }
3644
- function sha256Text(value) {
3645
- return createHash("sha256").update(value).digest("hex");
3646
- }
3647
- function normalizeForStableHash(value) {
3648
- if (Array.isArray(value)) return value.map((entry) => normalizeForStableHash(entry));
3649
- if (value && typeof value === "object") {
3650
- const record = value;
3651
- return Object.fromEntries(Object.keys(record).toSorted((left, right) => left.localeCompare(right)).map((key) => [key, normalizeForStableHash(record[key])]));
3652
- }
3653
- return value;
3654
- }
3655
- function stableJsonHash(value) {
3656
- return sha256Text(JSON.stringify(normalizeForStableHash(value)) ?? "null");
3657
- }
3658
- function buildCodexBootstrapInjectionStats(params) {
3659
- const injectedIndex = indexCodexContextFileContent(params.injectedFiles);
3660
- const developerInstructionIndex = indexCodexContextFileContent(params.developerInstructionFiles ?? []);
3661
- const memoryToolRoutedPaths = new Set((params.memoryToolRoutedBootstrapFiles ?? []).map((file) => readNonBlankString(file.path)).filter(isNonEmptyString$1).map(normalizeCodexContextFilePath));
3662
- return params.bootstrapFiles.map((file) => {
3663
- const fileName = readNonBlankString(file.name);
3664
- const pathValue = readNonBlankString(file.path) ?? fileName ?? "";
3665
- const displayName = (fileName ?? getCodexContextFileDisplayBasename(pathValue)) || pathValue;
3666
- const baseName = getCodexContextFileBasename(pathValue || fileName || "");
3667
- const rawChars = file.missing ? 0 : (file.content ?? "").trimEnd().length;
3668
- const memoryToolRoutedFile = baseName === CODEX_MEMORY_CONTEXT_BASENAME && params.memoryToolRouted === true && memoryToolRoutedPaths.has(normalizeCodexContextFilePath(pathValue));
3669
- const injected = memoryToolRoutedFile ? void 0 : readCodexIndexedContextFileContent(injectedIndex, pathValue, fileName) ?? readCodexIndexedContextFileContent(developerInstructionIndex, pathValue, fileName);
3670
- if (!file.missing && injected === void 0 && CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName)) return {
3671
- name: displayName,
3672
- path: pathValue,
3673
- missing: false,
3674
- rawChars,
3675
- injectionStatus: "native_unverified",
3676
- injectedChars: null,
3677
- truncated: null
3678
- };
3679
- const injectedChars = memoryToolRoutedFile ? 0 : injected?.length ?? 0;
3680
- const truncated = memoryToolRoutedFile ? false : !file.missing && injectedChars < rawChars;
3681
- return {
3682
- name: displayName,
3683
- path: pathValue,
3684
- missing: file.missing,
3685
- rawChars,
3686
- injectedChars,
3687
- truncated
3688
- };
3689
- });
3690
- }
3691
- function indexCodexContextFileContent(files) {
3692
- const byPath = /* @__PURE__ */ new Map();
3693
- const byBaseName = /* @__PURE__ */ new Map();
3694
- for (const file of files) {
3695
- const pathValue = readNonBlankString(file.path);
3696
- if (!pathValue) continue;
3697
- if (!byPath.has(pathValue)) byPath.set(pathValue, file.content);
3698
- const baseName = getCodexContextFileBasename(pathValue);
3699
- if (baseName && !byBaseName.has(baseName)) byBaseName.set(baseName, file.content);
3700
- }
3701
- return {
3702
- byPath,
3703
- byBaseName
3704
- };
3705
- }
3706
- function readCodexIndexedContextFileContent(index, pathValue, fileName) {
3707
- const pathContent = index.byPath.get(pathValue);
3708
- if (pathContent !== void 0) return pathContent;
3709
- if (fileName) {
3710
- const nameContent = index.byPath.get(fileName);
3711
- if (nameContent !== void 0) return nameContent;
3712
- }
3713
- const baseName = getCodexContextFileBasename(fileName ?? pathValue);
3714
- return baseName ? index.byBaseName.get(baseName) : void 0;
3715
- }
3716
- function readPositiveNumber(value) {
3717
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
3718
- }
3719
- /**
3720
- * Builds OpenClaw-provided workspace prompt context for the current Codex turn.
3721
- */
3722
- function buildCodexOpenClawPromptContext(params) {
3723
- if (!shouldInjectCodexOpenClawPromptContext(params.params)) return;
3724
- const sections = [params.workspacePromptContext?.trim() ? [
3725
- "## OpenClaw Workspace Context",
3726
- "",
3727
- params.workspacePromptContext.trim()
3728
- ].join("\n") : void 0, params.watchedSessionsContext?.trim() || void 0].filter(isNonEmptyString$1);
3729
- if (sections.length === 0) return;
3730
- return [
3731
- "OpenClaw runtime context for this turn:",
3732
- "Treat this OpenClaw-provided context as supporting project/user reference for the current request.",
3733
- "",
3734
- ...sections
3735
- ].join("\n");
3736
- }
3737
- /**
3738
- * Renders the watched-sessions block for the Codex per-turn runtime context.
3739
- * Codex builds its own instruction layers, so the embedded prompt's Watched
3740
- * Sessions section must be re-surfaced here or Codex-backed main sessions
3741
- * keep refusing cross-session questions (openclaw#114797).
3742
- */
3743
- function buildCodexWatchedSessionsContext(params) {
3744
- if (!shouldInjectCodexOpenClawPromptContext(params.attempt)) return;
3745
- return buildWatchedSessionsHarnessContext({
3746
- config: params.attempt.config,
3747
- sessionKey: params.sessionKey,
3748
- sandboxed: params.sandboxed,
3749
- toolNames: flattenCodexDynamicToolFunctions(params.dynamicTools).map((tool) => normalizeCodexDynamicToolName(tool.name))
3750
- });
3751
- }
3752
- function shouldInjectCodexOpenClawPromptContext(params) {
3753
- return !(params.bootstrapContextMode === "lightweight" && params.bootstrapContextRunKind === "cron");
3754
- }
3755
- /** Renders loaded OpenClaw skill prompts as Codex collaboration instructions. */
3756
- function renderCodexSkillsCollaborationInstructions(params) {
3757
- if (!shouldInjectCodexOpenClawPromptContext(params.attempt)) return;
3758
- return params.skillsPrompt?.trim() ? [
3759
- "## OpenClaw Skills",
3760
- "",
3761
- params.skillsPrompt.trim()
3762
- ].join("\n") : void 0;
3763
- }
3764
- /**
3765
- * Prepends OpenClaw context while preserving leading delivery metadata as
3766
- * routing guidance instead of user request text.
3767
- */
3768
- function prependCodexOpenClawPromptContext(prompt, context, options = {}) {
3769
- const { deliveryHint, prompt: promptWithoutDeliveryHint } = splitLeadingCodexDeliveryHint(prompt);
3770
- if (!context?.trim() && (!deliveryHint || options.preservePromptWithoutContext)) return prompt;
3771
- const promptSection = promptWithoutDeliveryHint.startsWith("OpenClaw assembled context for this turn:") ? promptWithoutDeliveryHint : ["Current user request:", promptWithoutDeliveryHint].join("\n");
3772
- const deliverySection = deliveryHint ? [
3773
- "OpenClaw delivery metadata:",
3774
- "This delivery metadata is runtime routing guidance, not the user's request.",
3775
- deliveryHint
3776
- ].join("\n") : void 0;
3777
- return [
3778
- context?.trim(),
3779
- deliverySection,
3780
- promptSection
3781
- ].filter(Boolean).join("\n\n");
3782
- }
3783
- /**
3784
- * Maps the surviving user-request portion of an input range after delivery
3785
- * metadata has been relocated before the request.
3786
- */
3787
- function resolveCodexDeliveryHintPreservedInputRange(params) {
3788
- const { prompt, promptInputRange, decoratedPrompt } = params;
3789
- const { deliveryHint, prompt: promptWithoutDeliveryHint } = splitLeadingCodexDeliveryHint(prompt);
3790
- if (!deliveryHint || !promptInputRange || promptInputRange.start < 0 || promptInputRange.end < promptInputRange.start || promptInputRange.end > prompt.length || !decoratedPrompt.endsWith(promptWithoutDeliveryHint)) return;
3791
- const promptWithoutDeliveryHintStart = prompt.length - promptWithoutDeliveryHint.length;
3792
- const inputStart = Math.max(promptInputRange.start, promptWithoutDeliveryHintStart);
3793
- const inputEnd = Math.max(inputStart, Math.min(promptInputRange.end, promptWithoutDeliveryHint.length + promptWithoutDeliveryHintStart));
3794
- const decoratedPromptSuffixStart = decoratedPrompt.length - promptWithoutDeliveryHint.length;
3795
- const requestHeader = "Current user request:\n";
3796
- const requestHeaderStart = decoratedPromptSuffixStart - 22;
3797
- return {
3798
- start: inputStart === promptWithoutDeliveryHintStart && decoratedPrompt.slice(requestHeaderStart, decoratedPromptSuffixStart) === requestHeader ? requestHeaderStart : decoratedPromptSuffixStart + inputStart - promptWithoutDeliveryHintStart,
3799
- end: decoratedPromptSuffixStart + inputEnd - promptWithoutDeliveryHintStart
3800
- };
3801
- }
3802
- function splitLeadingCodexDeliveryHint(prompt) {
3803
- const trimmedStart = prompt.trimStart();
3804
- const matchedHint = MESSAGE_TOOL_DELIVERY_HINTS.find((hint) => trimmedStart.startsWith(hint));
3805
- if (!matchedHint) return { prompt };
3806
- return {
3807
- deliveryHint: matchedHint,
3808
- prompt: trimmedStart.slice(matchedHint.length).replace(/^\s*\n/, "").trimStart()
3809
- };
3810
- }
3811
- function renderCodexWorkspaceBootstrapPromptContext(contextFiles) {
3812
- const files = contextFiles;
3813
- if (files.length === 0) return;
3814
- const lines = [
3815
- "OpenClaw loaded these user-editable workspace files for the current turn. Codex loads project-local AGENTS.md natively. When execution uses another folder, OpenClaw supplies the agent workspace AGENTS.md as thread-level developer instructions. SOUL.md, IDENTITY.md, and USER.md remain turn-scoped collaboration instructions. Those files are not repeated here.",
3816
- "",
3817
- "# Project Context",
3818
- "",
3819
- "The following project context files have been loaded:"
3820
- ];
3821
- lines.push("");
3822
- for (const file of files) lines.push(`## ${file.path}`, "", file.content, "");
3823
- return lines.join("\n").trim();
3824
- }
3825
- function selectCodexWorkspacePromptContextFiles(contextFiles, options = {}) {
3826
- const excludeMemory = options.excludeMemory ?? true;
3827
- return contextFiles.filter((file) => {
3828
- const baseName = getCodexContextFileBasename(file.path);
3829
- return baseName && !CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName) && !CODEX_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES.has(baseName) && (!excludeMemory || !isCodexWorkspaceRootMemoryContextFile({
3830
- file,
3831
- workspaceDir: options.memoryWorkspaceDir
3832
- })) && !isMissingCodexBootstrapContextFile(file);
3833
- }).toSorted(compareCodexContextFiles);
3834
- }
3835
- function selectCodexWorkspaceTurnScopedDeveloperInstructionFiles(contextFiles) {
3836
- return selectCodexWorkspaceDeveloperInstructionFiles(contextFiles, CODEX_TURN_SCOPED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES);
3837
- }
3838
- function selectCodexWorkspaceAgentProjectInstructionFiles(contextFiles, agentWorkspaceDir) {
3839
- const agentProjectDocPath = path.join(path.resolve(agentWorkspaceDir), "AGENTS.md");
3840
- return selectCodexWorkspaceDeveloperInstructionFiles(contextFiles, CODEX_NATIVE_PROJECT_DOC_BASENAMES).filter((file) => path.resolve(file.path) === agentProjectDocPath);
3841
- }
3842
- function selectCodexWorkspaceDeveloperInstructionFiles(contextFiles, basenames) {
3843
- return contextFiles.filter((file) => {
3844
- const baseName = getCodexContextFileBasename(file.path);
3845
- return baseName && basenames.has(baseName) && !isMissingCodexBootstrapContextFile(file) && file.content.trim().length > 0;
3846
- }).toSorted(compareCodexContextFiles);
3847
- }
3848
- function renderCodexWorkspaceCollaborationDeveloperInstructions(files) {
3849
- return renderCodexWorkspaceDeveloperInstructions({
3850
- files,
3851
- header: "## OpenClaw Agent Soul",
3852
- preamble: "OpenClaw loaded these workspace instruction files from the active agent workspace. They are the canonical definitions of who you are, how you think and work, and the human you work alongside. Internalize and follow them accordingly.",
3853
- wrapperTag: "AGENT_SOUL"
3854
- });
3855
- }
3856
- function renderCodexWorkspaceDeveloperInstructions(params) {
3857
- const { files, header, preamble, wrapperTag } = params;
3858
- if (files.length === 0) return;
3859
- const lines = [
3860
- header,
3861
- "",
3862
- preamble,
3863
- ""
3864
- ];
3865
- if (wrapperTag) lines.push(`<${wrapperTag}>`, "");
3866
- for (const file of files) lines.push(`### ${file.path}`, "", file.content, "");
3867
- if (wrapperTag) lines.push(`</${wrapperTag}>`);
3868
- return lines.join("\n").trim();
3869
- }
3870
- function selectCodexWorkspaceMemoryReferenceFiles(params) {
3871
- return params.bootstrapFiles.filter((file) => {
3872
- return isCodexWorkspaceRootMemoryBootstrapFile({
3873
- file,
3874
- workspaceDir: params.workspaceDir
3875
- }) && !file.missing && (file.content ?? "").trim().length > 0;
3876
- }).toSorted(compareCodexBootstrapFiles);
3877
- }
3878
- /**
3879
- * Renders a memory-file reference that points Codex at memory tools instead of
3880
- * embedding MEMORY.md contents.
3881
- */
3882
- function renderCodexWorkspaceMemoryReference(params) {
3883
- if (params.files.length === 0) return;
3884
- const lines = [
3885
- "## OpenClaw Workspace Memory",
3886
- "",
3887
- `MEMORY.md exists in the active agent workspace as a memory file, not an instruction file. OpenClaw does not paste its contents into native Codex turns; use ${(params.toolNames?.length ? params.toolNames : Array.from(CODEX_MEMORY_TOOL_NAMES)).join(" or ")} when durable memory is relevant and the tools are available.`,
3888
- ""
3889
- ];
3890
- for (const file of params.files) lines.push(`- ${file.path}`);
3891
- return lines.join("\n").trim();
3892
- }
3893
- async function renderCodexWorkspaceMemoryCollaborationInstructions(params) {
3894
- const sections = [params.memoryToolRouted ? await renderCodexMemoryRecallInstructions({
3895
- toolNames: params.toolNames,
3896
- citationsMode: params.citationsMode,
3897
- agentId: params.agentId,
3898
- agentSessionKey: params.agentSessionKey,
3899
- sandboxed: params.sandboxed
3900
- }) : void 0, renderCodexWorkspaceMemoryReference({
3901
- files: params.files,
3902
- toolNames: params.toolNames
3903
- })].filter(isNonEmptyString$1);
3904
- return sections.length > 0 ? sections.join("\n\n") : void 0;
3905
- }
3906
- async function renderCodexMemoryRecallInstructions(params) {
3907
- const memoryPrompt = await prepareMemorySystemPromptAddition({
3908
- availableTools: new Set(params.toolNames),
3909
- citationsMode: params.citationsMode,
3910
- agentId: params.agentId,
3911
- agentSessionKey: params.agentSessionKey,
3912
- sandboxed: params.sandboxed
3913
- });
3914
- if (!memoryPrompt) return;
3915
- return [memoryPrompt, renderCodexMemoryToolSearchBridge(params.toolNames)].filter(isNonEmptyString$1).join("\n").trim();
3916
- }
3917
- function renderCodexMemoryToolSearchBridge(toolNames) {
3918
- const memoryToolNames = toolNames.map((name) => normalizeCodexDynamicToolName(name)).filter((name) => CODEX_MEMORY_TOOL_NAMES.has(name)).toSorted();
3919
- if (memoryToolNames.length === 0) return;
3920
- return `Codex may expose ${memoryToolNames.join(" and ")} as deferred tools. When the memory guidance above calls for memory recall, use an already-loaded memory tool directly. If the needed memory tool is deferred and not currently callable, use \`tool_search\` to load it, then call that memory tool.`;
3921
- }
3922
- /** Lists available memory tool names understood by Codex workspace memory routing. */
3923
- function getCodexWorkspaceMemoryToolNames(tools) {
3924
- const availableToolNames = new Set(flattenCodexDynamicToolFunctions(tools).map((tool) => normalizeCodexDynamicToolName(tool.name)));
3925
- return Array.from(CODEX_MEMORY_TOOL_NAMES).filter((name) => availableToolNames.has(name));
3926
- }
3927
- function canRouteCodexWorkspaceMemoryThroughTools(params) {
3928
- if (!params.config) return false;
3929
- return isSameCodexWorkspacePath(resolveAgentWorkspaceDir(params.config, params.agentId), params.workspaceDir);
3930
- }
3931
- function isMissingCodexBootstrapContextFile(file) {
3932
- return file.content.trimStart().startsWith("[MISSING] Expected at:");
3933
- }
3934
- function toCodexEmbeddedContextFile(file) {
3935
- return {
3936
- path: readNonBlankString(file.path) ?? readNonBlankString(file.name) ?? "",
3937
- content: file.content ?? ""
3938
- };
3939
- }
3940
- function isCodexWorkspaceRootMemoryBootstrapFile(params) {
3941
- return isCodexWorkspaceRootMemoryPath({
3942
- filePath: readNonBlankString(params.file.path) ?? readNonBlankString(params.file.name) ?? "",
3943
- workspaceDir: params.workspaceDir
3944
- });
3945
- }
3946
- function isCodexWorkspaceRootMemoryContextFile(params) {
3947
- if (!params.workspaceDir) return false;
3948
- return isCodexWorkspaceRootMemoryPath({
3949
- filePath: params.file.path,
3950
- workspaceDir: params.workspaceDir
3951
- });
3952
- }
3953
- function isCodexWorkspaceRootMemoryPath(params) {
3954
- const filePath = params.filePath.trim();
3955
- if (!filePath) return false;
3956
- return (path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(params.workspaceDir, filePath)) === path.join(path.resolve(params.workspaceDir), "MEMORY.md");
3957
- }
3958
- function isSameCodexWorkspacePath(left, right) {
3959
- return path.resolve(left) === path.resolve(right);
3960
- }
3961
- /**
3962
- * Remaps bootstrap file paths from the resolved workspace to the effective Codex
3963
- * workspace while preserving platform path separators.
3964
- */
3965
- function remapCodexContextFilePath(params) {
3966
- const relativePath = path.relative(params.sourceWorkspaceDir, params.file.path);
3967
- if (!relativePath || relativePath === ".." || relativePath.startsWith(`..${path.sep}`) || path.isAbsolute(relativePath) || params.sourceWorkspaceDir === params.targetWorkspaceDir) return params.file;
3968
- const targetUsesPosixSeparators = params.targetWorkspaceDir.includes("/") && !params.targetWorkspaceDir.includes("\\");
3969
- const normalizedRelativePath = targetUsesPosixSeparators ? relativePath.replaceAll("\\", "/") : relativePath.replaceAll("/", "\\");
3970
- return {
3971
- ...params.file,
3972
- path: targetUsesPosixSeparators ? path.posix.join(params.targetWorkspaceDir, normalizedRelativePath) : path.win32.join(params.targetWorkspaceDir, normalizedRelativePath)
3475
+ effectiveCwd,
3476
+ appServer,
3477
+ sessionPermissionPolicy,
3478
+ nativeHookRelayEvents,
3479
+ runAbortController,
3480
+ terminalState,
3481
+ abortExplicitly,
3482
+ abortFromUpstream,
3483
+ resolveReviewerPolicyContext,
3484
+ resolveRuntimeOptionsForCurrentBinding,
3485
+ mutable,
3486
+ initialStartupBindingHadInactiveThreadBootstrap,
3487
+ initialInactiveThreadBootstrapBindingForcedFreshStart
3973
3488
  };
3974
3489
  }
3975
- function compareCodexContextFiles(left, right) {
3976
- const leftPath = normalizeCodexContextFilePath(left.path);
3977
- const rightPath = normalizeCodexContextFilePath(right.path);
3978
- const leftBase = getCodexContextFileBasename(left.path);
3979
- const rightBase = getCodexContextFileBasename(right.path);
3980
- const leftOrder = CODEX_BOOTSTRAP_CONTEXT_ORDER.get(leftBase) ?? Number.MAX_SAFE_INTEGER;
3981
- const rightOrder = CODEX_BOOTSTRAP_CONTEXT_ORDER.get(rightBase) ?? Number.MAX_SAFE_INTEGER;
3982
- if (leftOrder !== rightOrder) return leftOrder - rightOrder;
3983
- if (leftBase !== rightBase) return leftBase.localeCompare(rightBase);
3984
- return leftPath.localeCompare(rightPath);
3985
- }
3986
- function compareCodexBootstrapFiles(left, right) {
3987
- return compareCodexContextFiles(toCodexEmbeddedContextFile(left), toCodexEmbeddedContextFile(right));
3988
- }
3989
- function normalizeCodexContextFilePath(filePath) {
3990
- return filePath.trim().replaceAll("\\", "/").toLowerCase();
3991
- }
3992
- function getCodexContextFileDisplayBasename(filePath) {
3993
- return filePath.trim().replaceAll("\\", "/").split("/").pop()?.trim() ?? "";
3994
- }
3995
- function getCodexContextFileBasename(filePath) {
3996
- return normalizeCodexContextFilePath(filePath).split("/").pop() ?? "";
3997
- }
3998
- function normalizeCodexDynamicToolName(name) {
3999
- return name.trim().toLowerCase();
4000
- }
4001
- function isNonEmptyString$1(value) {
4002
- return typeof value === "string" && value.length > 0;
4003
- }
4004
3490
  //#endregion
4005
3491
  //#region extensions/codex/src/app-server/run-attempt-state.ts
4006
3492
  async function clearCodexBindingAfterInvalidImagePayload(bindingStore, identity, fields) {
@@ -4048,21 +3534,6 @@ function isCodexActiveCompactTurnError(error) {
4048
3534
  const codexErrorInfo = isJsonObject(data?.codexErrorInfo) ? data.codexErrorInfo : void 0;
4049
3535
  return (isJsonObject(codexErrorInfo?.activeTurnNotSteerable) ? codexErrorInfo.activeTurnNotSteerable : void 0)?.turnKind === "compact";
4050
3536
  }
4051
- function readCodexFinalizationHookNotification(notification, threadId, turnId) {
4052
- if (notification.method !== "hook/started" && notification.method !== "hook/completed") return;
4053
- const params = isJsonObject(notification.params) ? notification.params : void 0;
4054
- const run = params && isJsonObject(params.run) ? params.run : void 0;
4055
- if (params?.threadId !== threadId || params.turnId !== turnId || run?.eventName !== "stop" && run?.eventName !== "subagentStop" || typeof run.id !== "string" || !run.id) return;
4056
- if (notification.method === "hook/started") return {
4057
- phase: "started",
4058
- runId: run.id
4059
- };
4060
- return {
4061
- phase: "completed",
4062
- runId: run.id,
4063
- status: typeof run.status === "string" ? run.status : void 0
4064
- };
4065
- }
4066
3537
  function joinPresentSections(...sections) {
4067
3538
  return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
4068
3539
  }
@@ -4070,39 +3541,11 @@ function prependCurrentInboundContext(prompt, context) {
4070
3541
  const text = context?.text.trim();
4071
3542
  return text ? [neutralizeCodexExplicitMentionSigils(text), prompt].filter(Boolean).join("\n\n") : prompt;
4072
3543
  }
4073
- function buildCodexAppServerTimeoutDiagnostics(params) {
4074
- const readNonBlankDetailString = (key) => {
4075
- const value = params.details?.[key];
4076
- return hasNonEmptyString(value) ? value : void 0;
4077
- };
4078
- const activeAppServerTurnRequests = asFiniteNumber(params.details?.activeAppServerTurnRequests);
4079
- const activeTurnItemCount = asFiniteNumber(params.details?.activeTurnItemCount);
4080
- const terminalTurnNotificationQueued = asBoolean(params.details?.terminalTurnNotificationQueued);
4081
- const completionIdleWatchArmed = asBoolean(params.details?.completionIdleWatchArmed);
4082
- const assistantCompletionIdleWatchArmed = asBoolean(params.details?.assistantCompletionIdleWatchArmed);
4083
- const terminalIdleWatchArmed = asBoolean(params.details?.terminalIdleWatchArmed);
4084
- return {
4085
- ...params.idleMs !== void 0 ? { idleMs: params.idleMs } : {},
4086
- ...params.timeoutMs !== void 0 ? { timeoutMs: params.timeoutMs } : {},
4087
- ...params.lastActivityReason ? { lastActivityReason: params.lastActivityReason } : {},
4088
- ...readNonBlankDetailString("lastNotificationMethod") ? { lastNotificationMethod: readNonBlankDetailString("lastNotificationMethod") } : {},
4089
- ...readNonBlankDetailString("lastNotificationItemId") ? { lastNotificationItemId: readNonBlankDetailString("lastNotificationItemId") } : {},
4090
- ...readNonBlankDetailString("lastNotificationItemType") ? { lastNotificationItemType: readNonBlankDetailString("lastNotificationItemType") } : {},
4091
- ...readNonBlankDetailString("lastNotificationItemRole") ? { lastNotificationItemRole: readNonBlankDetailString("lastNotificationItemRole") } : {},
4092
- ...readNonBlankDetailString("lastAssistantTextPreview") ? { lastAssistantTextPreview: readNonBlankDetailString("lastAssistantTextPreview") } : {},
4093
- ...activeAppServerTurnRequests !== void 0 ? { activeAppServerTurnRequests } : {},
4094
- ...activeTurnItemCount !== void 0 ? { activeTurnItemCount } : {},
4095
- ...terminalTurnNotificationQueued !== void 0 ? { terminalTurnNotificationQueued } : {},
4096
- ...completionIdleWatchArmed !== void 0 ? { completionIdleWatchArmed } : {},
4097
- ...assistantCompletionIdleWatchArmed !== void 0 ? { assistantCompletionIdleWatchArmed } : {},
4098
- ...terminalIdleWatchArmed !== void 0 ? { terminalIdleWatchArmed } : {}
4099
- };
4100
- }
4101
3544
  //#endregion
4102
3545
  //#region extensions/codex/src/app-server/run-attempt-context.ts
4103
3546
  async function prepareCodexAttemptContext(runtime, attemptTools) {
4104
3547
  const { connection, runtimeParams, activeSessionId, activeSessionFile, buildActiveRunAttemptParams, effectiveContextWindowInfo, effectiveContextTokenBudget, effectiveRuntimeProviderId, effectiveRuntimeModelId, hookChannelId } = runtime;
4105
- const { params, sessionAgentId, contextSessionKey, activeContextEngine, initialStartupBindingHadInactiveThreadBootstrap, sandboxSessionKey, effectiveWorkspace, effectiveCwd, agentDir, usesSupervisionConnection, resolvedWorkspace, initialInactiveThreadBootstrapBindingForcedFreshStart, sandbox } = connection;
3548
+ const { params, sessionAgentId, contextSessionKey, activeContextEngine, initialStartupBindingHadInactiveThreadBootstrap, effectiveWorkspace, effectiveCwd, agentDir, usesSupervisionConnection, resolvedWorkspace, initialInactiveThreadBootstrapBindingForcedFreshStart, sandbox } = connection;
4106
3549
  const { toolBridge } = attemptTools;
4107
3550
  const activeTranscriptTarget = {
4108
3551
  agentId: sessionAgentId,
@@ -4128,12 +3571,20 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
4128
3571
  const hookContext = {
4129
3572
  runId: params.runId,
4130
3573
  agentId: sessionAgentId,
4131
- sessionKey: sandboxSessionKey,
3574
+ sessionKey: contextSessionKey,
4132
3575
  sessionId: params.sessionId,
4133
3576
  workspaceDir: params.workspaceDir,
4134
- messageProvider: params.messageProvider ?? void 0,
4135
3577
  trigger: params.trigger,
4136
- channelId: hookChannelId,
3578
+ ...buildAgentHookContextChannelFields({
3579
+ sessionKey: contextSessionKey,
3580
+ messageChannel: params.messageChannel,
3581
+ messageProvider: params.messageProvider,
3582
+ currentChannelId: hookChannelId,
3583
+ messageTo: params.messageTo,
3584
+ senderId: params.senderId,
3585
+ agentAccountId: params.agentAccountId
3586
+ }),
3587
+ channelContext: params.channelContext,
4137
3588
  ...hookContextWindowFields
4138
3589
  };
4139
3590
  const hookRunner = getAgentHarnessHookRunner();
@@ -4349,7 +3800,7 @@ function createCodexModelCallDiagnosticEmitter(params) {
4349
3800
  }
4350
3801
  /** Classifies model-call failures into timeout/abort buckets for diagnostics. */
4351
3802
  function classifyCodexModelCallFailureKind(params) {
4352
- if (params.timedOut || params.turnCompletionIdleTimedOut) return "timeout";
3803
+ if (params.timedOut) return "timeout";
4353
3804
  const errorMessage = params.error ? params.formatError(params.error).toLowerCase() : "";
4354
3805
  if (errorMessage.includes("timed out") || errorMessage.includes("timeout")) return "timeout";
4355
3806
  if (params.runAborted && !params.clientClosedAbort) return (typeof params.abortReason === "string" ? params.abortReason.toLowerCase() : params.abortReason ? params.formatError(params.abortReason).toLowerCase() : "").includes("timeout") ? "timeout" : "aborted";
@@ -4357,38 +3808,17 @@ function classifyCodexModelCallFailureKind(params) {
4357
3808
  }
4358
3809
  //#endregion
4359
3810
  //#region extensions/codex/src/app-server/attempt-results.ts
4360
- const CODEX_APP_SERVER_MISSING_TERMINAL_EVENT_USER_MESSAGE = "Codex stopped before confirming the turn was complete. The response may be incomplete; retry if needed.";
4361
- const CODEX_APP_SERVER_MISSING_TERMINAL_EVENT_SIDE_EFFECT_USER_MESSAGE = "Codex stopped before confirming the turn was complete. Some work may already have been performed; verify the current state before retrying.";
4362
- const CODEX_APP_SERVER_TERMINAL_IDLE_USER_MESSAGE = "Codex stopped responding: no activity arrived for the turn's liveness window, so the turn was ended and the connection was replaced. Retry to continue on a fresh session.";
4363
3811
  /** Joins terminal assistant text blocks into the final attempt answer. */
4364
3812
  function collectTerminalAssistantText(result) {
4365
3813
  return result.assistantTexts.join("\n\n").trim();
4366
3814
  }
4367
- /**
4368
- * Builds the user-facing timeout outcome when Codex stops without a terminal
4369
- * turn event.
4370
- */
4371
- function buildCodexAppServerPromptTimeoutOutcome(params) {
4372
- if (!params.turnCompletionIdleTimedOut) return;
4373
- if (params.turnWatchTimeoutKind === "terminal") {
4374
- if (collectTerminalAssistantText(params.result)) return;
4375
- const terminalReplayBlockedReason = resolveCodexAppServerReplayBlockedReason(params.result);
4376
- return {
4377
- message: CODEX_APP_SERVER_TERMINAL_IDLE_USER_MESSAGE,
4378
- ...terminalReplayBlockedReason ? {
4379
- replayInvalid: true,
4380
- livenessState: "abandoned"
4381
- } : {}
4382
- };
4383
- }
4384
- if (params.turnWatchTimeoutKind !== void 0 && params.turnWatchTimeoutKind !== "completion") return;
4385
- const replayBlockedReason = resolveCodexAppServerReplayBlockedReason(params.result);
3815
+ /** Reports the owner's deadline without guessing whether native work finished. */
3816
+ function buildCodexAppServerPromptTimeoutOutcome(timeout) {
3817
+ if (!timeout) return;
4386
3818
  return {
4387
- message: replayBlockedReason === "tool_activity" || replayBlockedReason === "potential_side_effect" || replayBlockedReason === "active_item" ? CODEX_APP_SERVER_MISSING_TERMINAL_EVENT_SIDE_EFFECT_USER_MESSAGE : CODEX_APP_SERVER_MISSING_TERMINAL_EVENT_USER_MESSAGE,
4388
- ...replayBlockedReason ? {
4389
- replayInvalid: true,
4390
- livenessState: "abandoned"
4391
- } : {}
3819
+ message: timeout.kind === "execution" ? "Codex reached the configured execution time limit. Some work may already have been performed; verify the current state before continuing." : "Codex finished its turn, but OpenClaw could not finish processing the result. Some work may already have been performed; verify the current state before continuing.",
3820
+ replayInvalid: true,
3821
+ livenessState: "abandoned"
4392
3822
  };
4393
3823
  }
4394
3824
  /** Explains why an incomplete app-server turn cannot be safely replayed. */
@@ -4436,101 +3866,17 @@ function isInvalidCodexImagePayloadError(message) {
4436
3866
  return /\b(?:invalid|malformed)\b[\s\S]{0,120}\b(?:image|image url|base64)\b/iu.test(normalizedMessage) || /\b(?:image|image url|base64)\b[\s\S]{0,120}\b(?:invalid|malformed)\b/iu.test(normalizedMessage);
4437
3867
  }
4438
3868
  //#endregion
4439
- //#region extensions/codex/src/app-server/settled-turn-context.ts
4440
- function collectUniqueMessageIdentities(messages) {
4441
- const identities = /* @__PURE__ */ new Map();
4442
- for (const [index, message] of messages.entries()) {
4443
- const identity = readMirrorIdentity(message);
4444
- if (!identity) continue;
4445
- if (identities.has(identity)) return;
4446
- identities.set(identity, index);
4447
- }
4448
- return identities;
4449
- }
4450
- function adoptPersistedHostPrompt(params) {
4451
- const promptIdentity = `${params.turnId}:prompt`;
4452
- if (params.mirroredMessages.some((message) => readMirrorIdentity(message) === promptIdentity)) return params;
4453
- const sourcePrompt = params.settledMessages[0];
4454
- const sourceKey = sourcePrompt?.idempotencyKey;
4455
- if (sourcePrompt?.role !== "user" || readMirrorIdentity(sourcePrompt) !== promptIdentity || typeof sourceKey !== "string" || sourceKey.trim().length === 0) return params;
4456
- const matches = params.historyMessages.flatMap((message, index) => message.role === "user" && message.idempotencyKey === sourceKey ? [{
4457
- index,
4458
- message
4459
- }] : []);
4460
- const persistedPrompt = matches.length === 1 ? matches[0] : void 0;
4461
- const persistedMetadata = persistedPrompt?.message;
4462
- if (!persistedPrompt || readMirrorIdentity(persistedPrompt.message) !== void 0 || readCodexMirrorSourceFingerprint(persistedPrompt.message) !== void 0 || persistedMetadata?.["__openclaw"]?.mirrorOrigin === "codex-app-server") return params;
4463
- const sourceUpstreamText = readUpstreamUserText(sourcePrompt);
4464
- const persistedUpstreamText = readUpstreamUserText(persistedPrompt.message);
4465
- if (persistedUpstreamText !== void 0 && persistedUpstreamText !== sourceUpstreamText) return params;
4466
- let logicalPrompt = attachCodexMirrorIdentity(persistedPrompt.message, promptIdentity);
4467
- if (sourceUpstreamText !== void 0) logicalPrompt = attachUpstreamUserText(logicalPrompt, sourceUpstreamText);
4468
- if (serializeCodexMirrorSourceEvidence(logicalPrompt) !== serializeCodexMirrorSourceEvidence(sourcePrompt)) return params;
4469
- const historyMessages = [...params.historyMessages];
4470
- historyMessages[persistedPrompt.index] = logicalPrompt;
4471
- return {
4472
- historyMessages,
4473
- mirroredMessages: [logicalPrompt, ...params.mirroredMessages]
4474
- };
4475
- }
4476
- /** Freezes one complete active transcript branch through the settled tool-result boundary. */
4477
- function buildCodexSettledTurnFinalizationContext(params) {
4478
- const { historyMessages, mirroredMessages } = adoptPersistedHostPrompt(params);
4479
- const boundaryMessage = params.settledMessages.findLast((message) => message.role === "toolResult");
4480
- const boundaryIdentity = boundaryMessage ? readMirrorIdentity(boundaryMessage) : void 0;
4481
- if (!boundaryMessage || !boundaryIdentity || !boundaryIdentity.startsWith(`${params.turnId}:tool:`)) return;
4482
- const settledBoundaryIndex = params.settledMessages.indexOf(boundaryMessage);
4483
- const requiredIdentities = params.settledMessages.slice(0, settledBoundaryIndex + 1).map(readMirrorIdentity);
4484
- if (requiredIdentities.length === 0 || requiredIdentities.some((identity) => !identity) || new Set(requiredIdentities).size !== requiredIdentities.length || !requiredIdentities.includes(`${params.turnId}:prompt`)) return;
4485
- const historyIdentities = collectUniqueMessageIdentities(historyMessages);
4486
- const mirroredIdentities = collectUniqueMessageIdentities(mirroredMessages);
4487
- if (!historyIdentities || !mirroredIdentities) return;
4488
- const mirroredBoundaryIndex = mirroredIdentities.get(boundaryIdentity);
4489
- if (mirroredBoundaryIndex === void 0) return;
4490
- const mirroredThroughBoundary = mirroredMessages.slice(0, mirroredBoundaryIndex + 1);
4491
- if (mirroredThroughBoundary.length !== requiredIdentities.length || mirroredThroughBoundary.some((message, index) => readMirrorIdentity(message) !== requiredIdentities[index])) return;
4492
- const historyBoundaryIndex = historyIdentities.get(boundaryIdentity);
4493
- if (historyBoundaryIndex === void 0) return;
4494
- let previousHistoryIndex = -1;
4495
- for (const mirroredMessage of mirroredThroughBoundary) {
4496
- const identity = readMirrorIdentity(mirroredMessage);
4497
- const historyIndex = identity ? historyIdentities.get(identity) : void 0;
4498
- const historyMessage = historyIndex === void 0 ? void 0 : historyMessages[historyIndex];
4499
- if (historyIndex === void 0 || historyIndex <= previousHistoryIndex || historyIndex > historyBoundaryIndex || !historyMessage || serializeCodexMirrorSourceEvidence(historyMessage) !== serializeCodexMirrorSourceEvidence(mirroredMessage)) return;
4500
- previousHistoryIndex = historyIndex;
4501
- }
4502
- return {
4503
- source: "openclaw-transcript",
4504
- messages: Object.freeze(structuredClone(params.historyMessages.slice(0, historyBoundaryIndex + 1)))
4505
- };
4506
- }
4507
- /** Reads and freezes the current active transcript branch after mirroring has settled. */
4508
- async function captureCodexSettledTurnFinalizationContext(params) {
4509
- try {
4510
- const historyMessages = await readCodexMirroredSessionHistoryMessages(params);
4511
- if (!historyMessages) return;
4512
- return buildCodexSettledTurnFinalizationContext({
4513
- historyMessages,
4514
- mirroredMessages: params.mirroredMessages,
4515
- settledMessages: params.settledMessages,
4516
- turnId: params.turnId
4517
- });
4518
- } catch (error) {
4519
- embeddedAgentLog.warn("codex settled-turn finalization context capture failed", {
4520
- error: formatErrorMessage(error),
4521
- turnId: params.turnId
4522
- });
4523
- return;
4524
- }
4525
- }
4526
- //#endregion
4527
3869
  //#region extensions/codex/src/app-server/trajectory.ts
4528
3870
  /** Creates a trajectory recorder when the host exposes its capture capability. */
4529
3871
  function createCodexTrajectoryRecorder(params) {
4530
3872
  if (!params.trajectory) return null;
4531
3873
  const trajectory = params.trajectory;
4532
3874
  return {
4533
- recordEvent: trajectory.recordEvent,
3875
+ recordEvent: (type, data) => {
3876
+ try {
3877
+ trajectory.recordEvent(type, data);
3878
+ } catch {}
3879
+ },
4534
3880
  flush: trajectory.flush
4535
3881
  };
4536
3882
  }
@@ -4594,39 +3940,44 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4594
3940
  const { effectiveRuntimeProviderId, effectiveRuntimeModelId } = runtime;
4595
3941
  const { params, terminalState, runAbortController, activeContextEngine, bindingStore, bindingIdentity, appServer, usesSupervisionConnection, sessionAgentId, contextSessionKey, effectiveCwd, agentDir, attemptStartedAt, startupAuthProfileId } = connection;
4596
3942
  const { toolBridge, toolState } = attemptTools;
4597
- const { state, completion, pendingOpenClawDynamicToolCompletionIds, activeTurnItemIds, activeCompletionBlockerItemIds, activeFinalizationHookRunIds, turnWatches } = turnRuntime;
3943
+ const { state, completion, deadlines } = turnRuntime;
4598
3944
  const { emitLifecycleTerminal, buildLifecycleTerminalMeta } = lifecycle;
4599
3945
  const { drainNotificationQueue } = notifications;
4600
3946
  const { codexModelCallDiagnostics } = requestRuntime;
4601
3947
  const { activeTurnId, activeProjector, streamState, freezeRunTerminalOutcome, notifyUserMessagePersisted } = activeTurn;
4602
3948
  await completion;
4603
- await drainNotificationQueue();
4604
- 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;
4605
- const hasRecoverableCompletedAssistant = !turnWatches.isCompletionIdleWatchPinnedByTerminalError() && turnWatches.isAssistantCompletionIdleWatchArmed() && hasQuiescentCompletedAssistant;
4606
- const recoveredTurnWatchTimeout = state.turnCompletionIdleTimedOut && !terminalState.explicitCancellationObserved && hasRecoverableCompletedAssistant && activeProjector.recoverCompletedTerminalAssistantAfterTurnWatchTimeout();
4607
- if (recoveredTurnWatchTimeout) {
4608
- embeddedAgentLog.warn("codex app-server recovered completed assistant output after missing turn completion", {
4609
- threadId: resourceState.thread.threadId,
4610
- turnId: activeTurnId,
4611
- timeoutKind: state.turnWatchTimeoutKind,
4612
- idleMs: state.turnWatchTimeoutIdleMs,
4613
- timeoutMs: state.turnWatchTimeoutMs
4614
- });
4615
- trajectoryRecorder?.recordEvent("turn.watch_timeout_recovered", {
4616
- threadId: resourceState.thread.threadId,
4617
- turnId: activeTurnId,
4618
- timeoutKind: state.turnWatchTimeoutKind,
4619
- idleMs: state.turnWatchTimeoutIdleMs,
4620
- timeoutMs: state.turnWatchTimeoutMs
4621
- });
3949
+ const abortGraceElapsed = createDeferred();
3950
+ let settlementClosed = false;
3951
+ let abortGraceTimer;
3952
+ const beginAbortGrace = () => {
3953
+ if (settlementClosed) return;
3954
+ abortGraceTimer = setTimeout(() => abortGraceElapsed.resolve(), TURN_FINALIZE_DRAIN_ABORT_GRACE_MS);
3955
+ abortGraceTimer.unref?.();
3956
+ };
3957
+ const abortListener = addAbortListener(runAbortController.signal, () => {
3958
+ state.abortCleanup.then(beginAbortGrace, beginAbortGrace);
3959
+ });
3960
+ const closeProjection = () => {
3961
+ state.projectionClosed = true;
3962
+ return activeProjector.closeProjection();
3963
+ };
3964
+ const settlement = drainNotificationQueue().then(closeProjection);
3965
+ try {
3966
+ await Promise.race([settlement, abortGraceElapsed.promise]);
3967
+ if (runAbortController.signal.aborted) await state.abortCleanup;
3968
+ } finally {
3969
+ settlementClosed = true;
3970
+ abortListener[Symbol.dispose]();
3971
+ clearTimeout(abortGraceTimer);
3972
+ deadlines.dispose();
3973
+ if (!state.projectionClosed) await resources.runCleanupStep("codex-transcript-checkpoint", closeProjection);
4622
3974
  }
4623
3975
  const result = activeProjector.buildResult(toolBridge.telemetry, { yieldDetected: toolState.yieldDetected });
4624
3976
  const projectedTerminal = attemptTerminal.project(result.terminal);
4625
- const effectiveTimedOut = state.timedOut && !recoveredTurnWatchTimeout;
4626
- const effectiveTurnCompletionIdleTimedOut = state.turnCompletionIdleTimedOut && !recoveredTurnWatchTimeout;
4627
- const isFinalAborted = () => terminalState.explicitCancellationObserved || !resourceState.executionDisconnectError && (projectedTerminal.aborted || runAbortController.signal.aborted && !state.clientClosedAbort && !recoveredTurnWatchTimeout);
4628
- const clientClosedPromptErrorForFinal = state.clientClosedPromptError && hasRecoverableCompletedAssistant ? void 0 : state.clientClosedPromptError;
4629
- let finalPromptError = resourceState.executionDisconnectError ?? clientClosedPromptErrorForFinal ?? (effectiveTurnCompletionIdleTimedOut ? state.turnCompletionIdleTimeoutMessage : effectiveTimedOut ? "codex app-server attempt timed out" : projectedTerminal.promptError);
3977
+ const effectiveTimedOut = state.timeout !== void 0;
3978
+ const isFinalAborted = () => terminalState.explicitCancellationObserved || !resourceState.executionDisconnectError && (projectedTerminal.aborted || runAbortController.signal.aborted && !state.clientClosedAbort);
3979
+ const clientClosedPromptErrorForFinal = state.clientClosedPromptError;
3980
+ let finalPromptError = resourceState.executionDisconnectError ?? clientClosedPromptErrorForFinal ?? (state.timeout?.kind === "settlement" ? "codex app-server terminal settlement timed out" : state.timeout?.kind === "execution" ? "codex app-server execution budget timed out" : projectedTerminal.promptError);
4630
3981
  const finalPromptErrorMessage = typeof finalPromptError === "string" ? finalPromptError : finalPromptError instanceof Error ? finalPromptError.message : finalPromptError ? formatErrorMessage(finalPromptError) : void 0;
4631
3982
  if (isInvalidCodexImagePayloadError(finalPromptErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(bindingStore, bindingIdentity, {
4632
3983
  phase: "turn_completed",
@@ -4669,33 +4020,23 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4669
4020
  });
4670
4021
  finalPromptError = resourceState.executionDisconnectError ?? finalPromptError;
4671
4022
  const finalPromptErrorSource = effectiveTimedOut || clientClosedPromptErrorForFinal ? "prompt" : projectedTerminal.promptErrorSource;
4672
- const codexAppServerFailureKind = clientClosedPromptErrorForFinal ? "client_closed_before_turn_completed" : effectiveTurnCompletionIdleTimedOut ? "turn_completion_idle_timeout" : void 0;
4023
+ const codexAppServerFailureKind = clientClosedPromptErrorForFinal ? "client_closed_before_turn_completed" : state.timeout?.kind === "settlement" ? "turn_settlement_timeout" : void 0;
4673
4024
  const replayBlockedReason = codexAppServerFailureKind ? resolveCodexAppServerReplayBlockedReason(result) : void 0;
4674
- const promptTimeoutOutcome = buildCodexAppServerPromptTimeoutOutcome({
4675
- result,
4676
- turnCompletionIdleTimedOut: effectiveTurnCompletionIdleTimedOut,
4677
- turnWatchTimeoutKind: state.turnWatchTimeoutKind
4678
- });
4679
- const failureDiagnostics = codexAppServerFailureKind === "client_closed_before_turn_completed" && state.clientClosedDiagnostic ? { transportError: state.clientClosedDiagnostic } : codexAppServerFailureKind === "turn_completion_idle_timeout" && state.turnWatchTimeoutKind === "completion" ? buildCodexAppServerTimeoutDiagnostics({
4680
- idleMs: state.turnWatchTimeoutIdleMs,
4681
- timeoutMs: state.turnWatchTimeoutMs,
4682
- lastActivityReason: state.turnWatchTimeoutLastActivityReason,
4683
- details: state.turnWatchTimeoutDetails
4684
- }) : void 0;
4025
+ const promptTimeoutOutcome = buildCodexAppServerPromptTimeoutOutcome(state.timeout);
4026
+ const failureDiagnostics = codexAppServerFailureKind === "client_closed_before_turn_completed" && state.clientClosedDiagnostic ? { transportError: state.clientClosedDiagnostic } : state.timeout?.kind === "settlement" ? { timeoutMs: state.timeout.timeoutMs } : void 0;
4685
4027
  const codexAppServerFailure = codexAppServerFailureKind ? {
4686
4028
  kind: codexAppServerFailureKind,
4687
- ...codexAppServerFailureKind === "turn_completion_idle_timeout" && state.turnWatchTimeoutKind ? { turnWatchTimeoutKind: state.turnWatchTimeoutKind } : {},
4688
4029
  transport: appServer.start.transport,
4689
4030
  threadId: resourceState.thread.threadId,
4690
4031
  turnId: activeTurnId,
4691
- replaySafe: replayBlockedReason === void 0,
4032
+ replaySafe: codexAppServerFailureKind === "client_closed_before_turn_completed" && replayBlockedReason === void 0,
4692
4033
  ...replayBlockedReason ? { replayBlockedReason } : {},
4693
4034
  ...failureDiagnostics ? { diagnostics: failureDiagnostics } : {}
4694
4035
  } : void 0;
4695
4036
  const finalAborted = isFinalAborted();
4696
4037
  const completedTurnStatus = activeProjector.getCompletedTurnStatus();
4697
- const locallyCompletedTurn = state.completed && (state.localCompletionRequested || !state.terminalTurnNotificationQueued) && !state.timedOut && clientClosedPromptErrorForFinal === void 0;
4698
- const turnSucceeded = !finalAborted && !effectiveTimedOut && (finalPromptError === null || finalPromptError === void 0) && (completedTurnStatus === "completed" || recoveredTurnWatchTimeout || locallyCompletedTurn);
4038
+ const locallyCompletedTurn = state.completed && state.localCompletionRequested && !state.timeout && clientClosedPromptErrorForFinal === void 0;
4039
+ const turnSucceeded = !finalAborted && !effectiveTimedOut && (finalPromptError === null || finalPromptError === void 0) && (completedTurnStatus === "completed" || locallyCompletedTurn);
4699
4040
  if (toolBridge.telemetry.messagingToolSentTargets.some((target) => target.sourceReplyFinal === true)) result.agentHarnessResultClassification = void 0;
4700
4041
  const attemptSucceeded = turnSucceeded && result.agentHarnessResultClassification === void 0;
4701
4042
  terminalState.turnSucceeded = turnSucceeded;
@@ -4709,7 +4050,6 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4709
4050
  const modelCallFailureKind = classifyCodexModelCallFailureKind({
4710
4051
  error: finalPromptError,
4711
4052
  timedOut: effectiveTimedOut,
4712
- turnCompletionIdleTimedOut: effectiveTurnCompletionIdleTimedOut,
4713
4053
  runAborted: finalAborted,
4714
4054
  abortReason: terminalState.explicitCancellationReason ?? runAbortController.signal.reason,
4715
4055
  clientClosedAbort: state.clientClosedAbort,
@@ -4729,14 +4069,13 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4729
4069
  turnId: activeTurnId
4730
4070
  });
4731
4071
  const { assistantTranscriptOwned, assistantTranscriptIdempotencyKey, terminalAnchor } = mirrorOutcome;
4732
- const shouldCaptureSettledTurnFinalizationContext = result.assistantTexts.every((text) => !text.trim()) && result.messagesSnapshot.some((message) => message.role === "toolResult") && (!finalPromptError || activeProjector.settledTurnFailureFinalizationAllowed);
4733
- const settledTurnFinalizationContext = shouldCaptureSettledTurnFinalizationContext ? await captureCodexSettledTurnFinalizationContext({
4072
+ const settledTurnFinalizationContext = result.assistantTexts.every((text) => !text.trim()) && result.messagesSnapshot.some((message) => message.role === "toolResult") && (!finalPromptError || activeProjector.settledTurnFailureFinalizationAllowed) ? await captureCodexSettledTurnFinalizationContext({
4734
4073
  ...activeTranscriptTarget,
4735
4074
  mirroredMessages: mirrorOutcome.mirroredMessages,
4736
4075
  settledMessages: result.messagesSnapshot,
4737
4076
  turnId: activeTurnId
4738
- }) : void 0;
4739
- if (shouldCaptureSettledTurnFinalizationContext && !settledTurnFinalizationContext) embeddedAgentLog.warn("codex settled-turn finalization context is unavailable", {
4077
+ }) ?? Object.freeze({ source: "unavailable" }) : void 0;
4078
+ if (settledTurnFinalizationContext?.source === "unavailable") embeddedAgentLog.warn("codex settled-turn finalization context is unavailable", {
4740
4079
  threadId: resourceState.thread.threadId,
4741
4080
  turnId: activeTurnId
4742
4081
  });
@@ -4839,8 +4178,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4839
4178
  yielded: toolState.yieldDetected
4840
4179
  })
4841
4180
  });
4842
- const finalizedResult = {
4843
- ...result,
4181
+ const finalizedResult = Object.assign(result, {
4844
4182
  ...toolState.yieldAcknowledgment ? { yieldAcknowledgment: toolState.yieldAcknowledgment } : {},
4845
4183
  terminal: attemptTerminal.normalize({
4846
4184
  timedOut: effectiveTimedOut,
@@ -4858,15 +4196,12 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
4858
4196
  ...resourceState.runtimeContinuationStarted ? { runtimeContinuationStarted: true } : {},
4859
4197
  ...!finalAborted && !effectiveTimedOut && !finalPromptError && preparedAuthBinding ? { authBindingFingerprint: preparedAuthBinding.fingerprint } : {},
4860
4198
  systemPromptReport
4861
- };
4199
+ });
4862
4200
  if (turnSucceeded && toolState.yieldDetected && !runAbortController.signal.aborted) resourceState.nativeHookRelay?.authorizeRetentionAfterSuccessfulYield();
4863
4201
  return finalizedResult;
4864
4202
  }
4865
4203
  //#endregion
4866
4204
  //#region extensions/codex/src/app-server/attempt-notification-state.ts
4867
- /**
4868
- * State machine for Codex app-server turn notifications and idle-watch updates.
4869
- */
4870
4205
  /** Emits coarse execution phases exactly once from app-server notifications. */
4871
4206
  function reportCodexExecutionNotification(params) {
4872
4207
  const { notification } = params;
@@ -4890,81 +4225,7 @@ function reportCodexExecutionNotification(params) {
4890
4225
  }
4891
4226
  /** Returns true when a notification ends the current app-server turn. */
4892
4227
  function isTerminalCodexTurnNotificationForTurn(params) {
4893
- if (!isCodexNotificationForTurn(params.notification.params, params.threadId, params.turnId)) return false;
4894
- return params.notification.method === "turn/completed";
4895
- }
4896
- /**
4897
- * Applies one notification to active item tracking, idle watches, and terminal
4898
- * turn state.
4899
- */
4900
- function applyCodexTurnNotificationState(params) {
4901
- const { notification, turnWatches } = params;
4902
- const isCurrentTurnNotification = isCodexNotificationForTurn(notification.params, params.threadId, params.turnId);
4903
- const isTurnCompletion = notification.method === "turn/completed" && isCurrentTurnNotification;
4904
- let turnCrossedToolHandoff = params.turnCrossedToolHandoff;
4905
- if (isCurrentTurnNotification) {
4906
- updateActiveTurnItemIds(notification, params.activeTurnItemIds);
4907
- updateActiveCompletionBlockerItemIds(notification, params.activeCompletionBlockerItemIds);
4908
- turnWatches.touchActivity(`notification:${notification.method}`, {
4909
- details: describeNotificationActivity(notification),
4910
- attemptProgress: true
4911
- });
4912
- params.onReportExecutionNotification(notification);
4913
- if (notification.method === "item/completed" && params.activeTurnItemIds.size === 0) params.onScheduleTerminalDynamicToolReleaseCheck();
4914
- }
4915
- const unblockedAssistantCompletionRelease = isCurrentTurnNotification && turnWatches.isAssistantCompletionIdleWatchArmed() && notification.method === "item/completed" && params.activeTurnItemIds.size === 0;
4916
- const trackedDynamicToolCompletion = isPendingOpenClawDynamicToolCompletionNotification(notification, params.pendingOpenClawDynamicToolCompletionIds);
4917
- const rawToolOutputCompletion = isRawToolOutputCompletionNotification(notification);
4918
- if (isCurrentTurnNotification && (rawToolOutputCompletion || isNativeToolProgressNotification(notification))) turnCrossedToolHandoff = true;
4919
- const assistantCompletionCanRelease = isAssistantCompletionReleaseNotification(notification, turnCrossedToolHandoff);
4920
- const postToolProgressNeedsTerminalGuard = isCurrentTurnNotification && turnCrossedToolHandoff && ((isRawAssistantProgressNotification(notification) || isRawReasoningCompletionNotification(notification)) && params.activeTurnItemIds.size === 0 || isReasoningProgressNotification(notification));
4921
- const postToolPatchUpdateNeedsTerminalGuard = isCurrentTurnNotification && turnCrossedToolHandoff && isFileChangePatchUpdatedNotification(notification);
4922
- const rawResponseItemCompletedWithNoActiveItems = isCurrentTurnNotification && notification.method === "rawResponseItem/completed" && params.activeTurnItemIds.size === 0 && params.activeAppServerTurnRequests === 0 && !assistantCompletionCanRelease && !postToolProgressNeedsTerminalGuard && !rawToolOutputCompletion;
4923
- const shouldArmNoToolPostProgressReplyWatch = isCurrentTurnNotification && !turnCrossedToolHandoff && params.activeTurnItemIds.size === 0 && (isReasoningItemCompletionNotification(notification) || isAssistantCommentaryCompletionNotification(notification));
4924
- const shouldArmNoToolPostRawProgressReplyWatch = !turnCrossedToolHandoff && rawResponseItemCompletedWithNoActiveItems && (isRawReasoningCompletionNotification(notification) || isRawAssistantProgressNotification(notification));
4925
- const shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem = isCurrentTurnNotification && notification.method === "item/completed" && params.activeTurnItemIds.size === 0 && !trackedDynamicToolCompletion && !assistantCompletionCanRelease && !shouldArmNoToolPostProgressReplyWatch;
4926
- const shouldUsePostToolContinuationWatch = turnCrossedToolHandoff && (postToolProgressNeedsTerminalGuard || postToolPatchUpdateNeedsTerminalGuard || rawToolOutputCompletion || trackedDynamicToolCompletion || shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem);
4927
- const armPostToolContinuationWatch = () => {
4928
- turnWatches.armCompletionIdleWatch({ timeoutMs: params.postToolRawAssistantCompletionIdleTimeoutMs });
4929
- turnWatches.extendAttemptIdleWatch(params.postToolRawAssistantCompletionIdleTimeoutMs);
4930
- };
4931
- const armPostProgressReplyWatch = () => {
4932
- turnWatches.armCompletionIdleWatch({ timeoutMs: CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS });
4933
- turnWatches.extendAttemptIdleWatch(CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS);
4934
- };
4935
- if (isCurrentTurnNotification && notification.method === "error") {
4936
- if (isRetryableErrorNotification(notification.params)) turnWatches.disarmCompletionIdleWatch();
4937
- else turnWatches.armCompletionIdleWatch({ pinnedByTerminalError: true });
4938
- turnWatches.disarmAssistantCompletionIdleWatch();
4939
- } else if (isTurnCompletion) turnWatches.disarmAssistantCompletionIdleWatch();
4940
- else if (isCurrentTurnNotification && assistantCompletionCanRelease) turnWatches.armAssistantCompletionIdleWatch(describeNotificationActivity(notification));
4941
- else if (postToolProgressNeedsTerminalGuard || postToolPatchUpdateNeedsTerminalGuard) armPostToolContinuationWatch();
4942
- else if (shouldArmNoToolPostProgressReplyWatch || shouldArmNoToolPostRawProgressReplyWatch) armPostProgressReplyWatch();
4943
- else if (trackedDynamicToolCompletion) armPostToolContinuationWatch();
4944
- else if (unblockedAssistantCompletionRelease) turnWatches.armAssistantCompletionIdleWatch(describeNotificationActivity(notification));
4945
- else if (shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem) if (shouldUsePostToolContinuationWatch) armPostToolContinuationWatch();
4946
- else turnWatches.armCompletionIdleWatch();
4947
- else if (rawResponseItemCompletedWithNoActiveItems) turnWatches.armCompletionIdleWatch();
4948
- else if (isCurrentTurnNotification && rawToolOutputCompletion) armPostToolContinuationWatch();
4949
- else if (isCurrentTurnNotification && shouldDisarmAssistantCompletionIdleWatch(notification)) turnWatches.disarmAssistantCompletionIdleWatch();
4950
- if (turnWatches.isCompletionIdleWatchArmed() && !turnWatches.isCompletionIdleWatchPinnedByTerminalError() && notification.method !== "turn/completed" && isCurrentTurnNotification && !trackedDynamicToolCompletion && !rawToolOutputCompletion && !postToolProgressNeedsTerminalGuard && !postToolPatchUpdateNeedsTerminalGuard && !rawResponseItemCompletedWithNoActiveItems && !shouldArmNoToolPostProgressReplyWatch && !shouldArmNoToolPostRawProgressReplyWatch && !shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem) turnWatches.disarmCompletionIdleWatch();
4951
- if (trackedDynamicToolCompletion) {
4952
- const itemId = readNotificationItemId(notification);
4953
- if (itemId) {
4954
- params.pendingOpenClawDynamicToolCompletionIds.delete(itemId);
4955
- params.onScheduleTerminalDynamicToolReleaseCheck();
4956
- }
4957
- }
4958
- return {
4959
- isCurrentTurnNotification,
4960
- isTurnAbortMarker: isCurrentTurnNotification && isCodexTurnAbortMarkerNotification(notification, { currentPromptTexts: params.currentPromptTexts }),
4961
- isTurnTerminal: isTerminalCodexTurnNotificationForTurn({
4962
- notification,
4963
- threadId: params.threadId,
4964
- turnId: params.turnId
4965
- }),
4966
- turnCrossedToolHandoff
4967
- };
4228
+ return params.notification.method === "turn/completed" && isCodexNotificationForTurn(params.notification.params, params.threadId, params.turnId) && readCodexTurnCompletedNotification(params.notification.params) !== void 0;
4968
4229
  }
4969
4230
  //#endregion
4970
4231
  //#region extensions/codex/src/app-server/run-attempt-lifecycle-terminal.ts
@@ -5058,7 +4319,7 @@ function createCodexAttemptLifecycleController(resources, turnRuntime) {
5058
4319
  state.lifecycleStarted = true;
5059
4320
  };
5060
4321
  const emitLifecycleTerminal = (data) => {
5061
- if (!state.lifecycleStarted || state.lifecycleTerminalEmitted) return;
4322
+ if (!state.lifecycleStarted || state.lifecycleTerminalEmitted || state.permissionChangeRestart) return;
5062
4323
  emitCodexAppServerEvent(params, {
5063
4324
  stream: "lifecycle",
5064
4325
  data: {
@@ -5159,10 +4420,9 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
5159
4420
  const { prompt, state: resourceState, projectorRef, registerNativeSubagentMonitor } = resources;
5160
4421
  const { context, turnState } = prompt;
5161
4422
  const { attemptTools, runtime } = context;
5162
- const { connection } = runtime;
5163
- const { appServer, runAbortController } = connection;
4423
+ const { appServer, runAbortController } = runtime.connection;
5164
4424
  const { allocateCodexToolOutcomeOrdinal } = attemptTools;
5165
- const { state, turnIdRef, userInputBridgeRef, steeringQueueRef, turnWatches, activeTurnItemIds, activeCompletionBlockerItemIds, activeFinalizationHookRunIds, finalizationHookBatchStatuses, pendingOpenClawDynamicToolCompletionIds, postToolRawAssistantCompletionIdleTimeoutMs, completeTurn } = turnRuntime;
4425
+ const { state, turnIdRef, userInputBridgeRef, steeringQueueRef, activeTurnItemIds, pendingOpenClawDynamicToolCompletionIds, completeTurn, noteProgress, deadlines } = turnRuntime;
5166
4426
  const { scheduleTerminalDynamicToolReleaseCheck, reportExecutionNotification, maybeAnnounceFastModeAutoOff } = lifecycle;
5167
4427
  const isTerminalTurnNotificationForTurn = (notification, notificationTurnId) => isTerminalCodexTurnNotificationForTurn({
5168
4428
  notification,
@@ -5170,81 +4430,47 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
5170
4430
  turnId: notificationTurnId
5171
4431
  });
5172
4432
  const handleNotification = async (notification) => {
4433
+ if (state.projectionClosed) return;
5173
4434
  const projector = projectorRef.current;
5174
4435
  const turnId = turnIdRef.current;
5175
- const steeringQueue = steeringQueueRef.current;
5176
4436
  userInputBridgeRef.current?.handleNotification(notification);
5177
4437
  if (!projector || !turnId) {
5178
4438
  if (notification.method === "error") state.latestStartupErrorNotification = notification;
5179
4439
  return;
5180
4440
  }
5181
- if ((state.timedOut || state.localCompletionRequested) && notification.method === "turn/completed" && readCodexTurnCompletedNotification(notification.params)?.turn.status === "interrupted") {
4441
+ const isCurrentTurn = isCodexNotificationForTurn(notification.params, resourceState.thread.threadId, turnId);
4442
+ const isTerminal = isTerminalTurnNotificationForTurn(notification, turnId);
4443
+ if ((state.timeout || state.localCompletionRequested) && isTerminal && readCodexTurnCompletedNotification(notification.params)?.turn.status === "interrupted") {
5182
4444
  completeTurn();
5183
4445
  return;
5184
4446
  }
5185
- const notificationState = applyCodexTurnNotificationState({
5186
- notification,
5187
- threadId: resourceState.thread.threadId,
5188
- turnId,
5189
- currentPromptTexts: [turnState.codexTurnPromptText],
5190
- turnWatches,
5191
- activeTurnItemIds,
5192
- activeCompletionBlockerItemIds,
5193
- activeAppServerTurnRequests: state.activeAppServerTurnRequests,
5194
- pendingOpenClawDynamicToolCompletionIds,
5195
- turnCrossedToolHandoff: state.turnCrossedToolHandoff,
5196
- postToolRawAssistantCompletionIdleTimeoutMs,
5197
- onScheduleTerminalDynamicToolReleaseCheck: scheduleTerminalDynamicToolReleaseCheck,
5198
- onReportExecutionNotification: reportExecutionNotification
5199
- });
5200
- state.turnCrossedToolHandoff = notificationState.turnCrossedToolHandoff;
5201
- if (notificationState.isCurrentTurnNotification && notification.method === "item/completed") {
5202
- const item = readCodexNotificationItem(notification.params);
5203
- if (item?.type === "userMessage" && typeof item.clientId === "string") await steeringQueue?.confirmConsumed(item.clientId);
5204
- }
5205
- if (notificationState.isTurnAbortMarker) state.sawCodexInterruptMarker = true;
5206
- const hookNotification = readCodexFinalizationHookNotification(notification, resourceState.thread.threadId, turnId);
5207
- if (hookNotification?.phase === "started") {
5208
- if (activeFinalizationHookRunIds.size === 0) finalizationHookBatchStatuses.clear();
5209
- activeFinalizationHookRunIds.add(hookNotification.runId);
5210
- turnWatches.disarmAssistantCompletionIdleWatch();
5211
- }
5212
- if (notificationState.isTurnTerminal) state.terminalTurnNotificationQueued = true;
4447
+ state.activeLocalProjections += 1;
5213
4448
  try {
5214
- await projector.handleNotification(notification);
5215
- const canRelease = isAssistantCompletionReleaseNotification(notification, state.turnCrossedToolHandoff) || notificationState.isCurrentTurnNotification && state.turnCrossedToolHandoff && notification.method === "rawResponseItem/completed" && projector.canReleaseLatestTerminalAssistantAfterToolHandoff();
5216
- if (notificationState.isCurrentTurnNotification && canRelease) {
5217
- const itemId = projector.getLatestTerminalAssistantCandidate()?.itemId;
5218
- if (state.rejectedFinalizationHookAssistant && itemId && itemId !== state.rejectedFinalizationHookAssistant.itemId) state.rejectedFinalizationHookAssistant = void 0;
5219
- else if (state.rejectedFinalizationHookAssistant) turnWatches.disarmAssistantCompletionIdleWatch();
5220
- else if (activeFinalizationHookRunIds.size === 0 && !state.terminalTurnNotificationQueued && state.activeAppServerTurnRequests === 0 && activeTurnItemIds.size === 0 && activeCompletionBlockerItemIds.size === 0 && pendingOpenClawDynamicToolCompletionIds.size === 0 && projector.hasLatestTerminalAssistantCandidateText()) turnWatches.armAssistantCompletionIdleWatch(describeNotificationActivity(notification));
4449
+ if (isCurrentTurn) {
4450
+ updateActiveTurnItemIds(notification, activeTurnItemIds);
4451
+ noteProgress(`notification:${notification.method}`);
4452
+ reportExecutionNotification(notification);
4453
+ if (isPendingOpenClawDynamicToolCompletionNotification(notification, pendingOpenClawDynamicToolCompletionIds)) {
4454
+ const itemId = readNotificationItemId(notification);
4455
+ if (itemId) pendingOpenClawDynamicToolCompletionIds.delete(itemId);
4456
+ }
4457
+ if (notification.method === "item/completed") {
4458
+ if (activeTurnItemIds.size === 0) scheduleTerminalDynamicToolReleaseCheck();
4459
+ const item = readCodexNotificationItem(notification.params);
4460
+ if (item?.type === "userMessage" && typeof item.clientId === "string") await steeringQueueRef.current?.confirmConsumed(item.clientId);
4461
+ }
4462
+ if (isCodexTurnAbortMarkerNotification(notification, { currentPromptTexts: [turnState.codexTurnPromptText] })) state.sawCodexInterruptMarker = true;
5221
4463
  }
5222
- if (notificationState.isCurrentTurnNotification && activeTurnItemIds.size === 0 && isRawFunctionToolOutputCompletionNotification(notification)) await maybeAnnounceFastModeAutoOff();
4464
+ await projector.handleNotification(notification);
4465
+ if (isCurrentTurn && activeTurnItemIds.size === 0 && isRawFunctionToolOutputCompletionNotification(notification)) await maybeAnnounceFastModeAutoOff();
5223
4466
  } catch (error) {
5224
4467
  embeddedAgentLog.debug("codex app-server projector notification threw", {
5225
4468
  method: notification.method,
5226
4469
  error
5227
4470
  });
5228
4471
  } finally {
5229
- if (hookNotification?.phase === "completed") {
5230
- state.unsettledFinalizationHookCount = Math.max(0, state.unsettledFinalizationHookCount - 1);
5231
- activeFinalizationHookRunIds.delete(hookNotification.runId);
5232
- finalizationHookBatchStatuses.set(hookNotification.runId, hookNotification.status);
5233
- if (activeFinalizationHookRunIds.size === 0) {
5234
- const statuses = new Set(finalizationHookBatchStatuses.values());
5235
- if (statuses.has("blocked") && !statuses.has("stopped")) {
5236
- const itemId = projector.getLatestTerminalAssistantCandidate()?.itemId;
5237
- state.rejectedFinalizationHookAssistant = itemId ? { itemId } : {};
5238
- turnWatches.disarmAssistantCompletionIdleWatch();
5239
- } else state.rejectedFinalizationHookAssistant = void 0;
5240
- }
5241
- if (activeFinalizationHookRunIds.size === 0 && state.rejectedFinalizationHookAssistant === void 0 && !state.terminalTurnNotificationQueued && state.activeAppServerTurnRequests === 0 && activeTurnItemIds.size === 0 && activeCompletionBlockerItemIds.size === 0 && pendingOpenClawDynamicToolCompletionIds.size === 0 && projector.hasLatestTerminalAssistantCandidateText()) turnWatches.armAssistantCompletionIdleWatch({
5242
- lastNotificationMethod: notification.method,
5243
- hookRunId: hookNotification.runId,
5244
- hookStatus: hookNotification.status
5245
- });
5246
- }
5247
- if (notificationState.isTurnTerminal) {
4472
+ state.activeLocalProjections -= 1;
4473
+ if (isTerminal) {
5248
4474
  if ((readCodexTurnCompletedNotification(notification.params)?.turn)?.status === "interrupted" && state.sawCodexInterruptMarker) projector.markAborted();
5249
4475
  completeTurn();
5250
4476
  }
@@ -5269,10 +4495,12 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
5269
4495
  const noteNotificationReceived = (notification, scope, receivedAtMs) => {
5270
4496
  const projector = projectorRef.current;
5271
4497
  const turnId = turnIdRef.current;
5272
- if (!projector || !turnId) return;
4498
+ if (!projector || !turnId || state.projectionClosed) return;
4499
+ projector.recordMcpToolCallReceipt(notification);
5273
4500
  if (isTerminalTurnNotificationForTurn(notification, turnId)) {
5274
4501
  state.terminalTurnNotificationQueued = true;
5275
4502
  steeringQueueRef.current?.sealAdmission();
4503
+ deadlines.beginSettlement(receivedAtMs);
5276
4504
  }
5277
4505
  if (scope.turnId === turnId) {
5278
4506
  const modelToolCallId = readRawResponseToolCallId(notification);
@@ -5280,11 +4508,6 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
5280
4508
  const nativeItem = readCodexNotificationItem(notification.params);
5281
4509
  if (nativeItem?.type === "webSearch") projector.recordNativeToolOutcome(nativeItem);
5282
4510
  }
5283
- if (readCodexFinalizationHookNotification(notification, resourceState.thread.threadId, turnId)?.phase === "started") {
5284
- state.unsettledFinalizationHookCount += 1;
5285
- turnWatches.disarmAssistantCompletionIdleWatch();
5286
- }
5287
- turnWatches.noteNotificationReceived(notification.method, { receivedAtMs });
5288
4511
  };
5289
4512
  const enqueueNotification = async (notification, scope) => {
5290
4513
  embeddedAgentLog.trace("codex app-server raw notification received", {
@@ -5306,7 +4529,6 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
5306
4529
  }
5307
4530
  //#endregion
5308
4531
  //#region extensions/codex/src/app-server/run-attempt-prompt.ts
5309
- const CODEX_META_KEY = "__openclaw";
5310
4532
  function isRestrictivePromptToolsAllow(toolsAllow) {
5311
4533
  return toolsAllow !== void 0 && !toolsAllow.some((name) => name.trim() === "*");
5312
4534
  }
@@ -5405,7 +4627,7 @@ async function prepareCodexAttemptPrompt(context) {
5405
4627
  if (isRestrictivePromptToolsAllow(toolsAllow)) throw new Error("Codex app-server cannot enforce before_prompt_build toolsAllow; use the embedded or Copilot runtime for turn-scoped tool policy.");
5406
4628
  return promptState.developerInstructions;
5407
4629
  } },
5408
- messages: structuredClone(historyState.messages),
4630
+ messages: historyState.messages,
5409
4631
  ctx: hookContext,
5410
4632
  bootstrapContextRunKind: params.bootstrapContextRunKind,
5411
4633
  toolAuthority: {
@@ -5483,11 +4705,9 @@ async function prepareCodexAttemptPrompt(context) {
5483
4705
  const cutoff = Date.parse(binding.historyCoveredThrough ?? "");
5484
4706
  return historyState.messages.filter((message) => {
5485
4707
  if (message.role !== "user" && message.role !== "assistant") return false;
5486
- const meta = CODEX_META_KEY in message ? message[CODEX_META_KEY] : void 0;
5487
- const mirrorIdentity = meta && typeof meta === "object" && !Array.isArray(meta) ? meta.mirrorIdentity : void 0;
5488
- const mirrorOrigin = meta && typeof meta === "object" && !Array.isArray(meta) ? meta.mirrorOrigin : void 0;
4708
+ const mirrorIdentity = readMirrorIdentity(message);
5489
4709
  const timestamp = typeof message.timestamp === "number" ? message.timestamp : typeof message.timestamp === "string" ? Date.parse(message.timestamp) : NaN;
5490
- return !("idempotencyKey" in message && typeof message.idempotencyKey === "string" && message.idempotencyKey.startsWith("codex-app-server:")) && mirrorOrigin !== "codex-app-server" && !(typeof mirrorIdentity === "string" && mirrorIdentity.startsWith("codex-app-server:")) && Number.isFinite(timestamp) && timestamp > (Number.isFinite(cutoff) ? cutoff : 0);
4710
+ return !("idempotencyKey" in message && typeof message.idempotencyKey === "string" && message.idempotencyKey.startsWith("codex-app-server:")) && !hasCodexMirrorOrigin(message) && !mirrorIdentity?.startsWith("codex-app-server:") && Number.isFinite(timestamp) && timestamp > (Number.isFinite(cutoff) ? cutoff : 0);
5491
4711
  });
5492
4712
  };
5493
4713
  const applyResumeStaleBindingContinuityProjection = (binding) => {
@@ -5519,7 +4739,8 @@ async function prepareCodexAttemptPrompt(context) {
5519
4739
  return projected;
5520
4740
  };
5521
4741
  const applyNoContextEngineContinuityProjection = (action, binding) => {
5522
- if (activeContextEngine || !historyState.messages.some((message) => message.role === "user")) return false;
4742
+ const hasContinuity = historyState.messages.some((message) => message.role === "user" || action === "started" && (message.role === "compactionSummary" || message.role === "branchSummary"));
4743
+ if (activeContextEngine || !hasContinuity) return false;
5523
4744
  if (action === "resumed" && promptState.precomputedStaleBindingContinuityProjectionApplied) return true;
5524
4745
  if (action === "started" && promptState.staleBindingContinuityForcedFreshStart) return true;
5525
4746
  if (action === "started" && promptState.inactiveThreadBootstrapBindingForcedFreshStart) return false;
@@ -5612,7 +4833,7 @@ function prepareCodexAttemptResources(prompt) {
5612
4833
  const { context, turnState, buildRenderedCodexDeveloperInstructions } = prompt;
5613
4834
  const { runtime, attemptTools } = context;
5614
4835
  const { connection, hookChannelId } = runtime;
5615
- const { appServer, params, effectiveCwd, sessionAgentId, sandboxSessionKey, runAbortController, sandbox, options, nativeHookRelayEvents } = connection;
4836
+ const { appServer, params, effectiveCwd, sessionAgentId, contextSessionKey, runAbortController, sandbox, options, nativeHookRelayEvents } = connection;
5616
4837
  const { toolBridge } = attemptTools;
5617
4838
  const trajectoryRecorder = createCodexTrajectoryRecorder({
5618
4839
  attempt: params,
@@ -5652,7 +4873,7 @@ function prepareCodexAttemptResources(prompt) {
5652
4873
  emitCodexNativePreToolUseFailureDiagnostic({
5653
4874
  agentId: sessionAgentId,
5654
4875
  sessionId: params.sessionId,
5655
- sessionKey: sandboxSessionKey,
4876
+ sessionKey: contextSessionKey,
5656
4877
  runId: params.runId,
5657
4878
  signal: runAbortController.signal,
5658
4879
  failure,
@@ -5774,8 +4995,10 @@ function prepareCodexAttemptResources(prompt) {
5774
4995
  events: nativeHookRelayEvents,
5775
4996
  agentId: sessionAgentId,
5776
4997
  sessionId: params.sessionId,
5777
- sessionKey: sandboxSessionKey,
4998
+ sessionKey: contextSessionKey,
5778
4999
  config: params.config,
5000
+ autoApproveMcpTools: shouldAutoApproveCodexAppServerApprovals(appServer),
5001
+ projectedMcpServers: runtime.bundleMcpThreadConfig.configPatch?.mcp_servers,
5779
5002
  runId: params.runId,
5780
5003
  channelId: hookChannelId,
5781
5004
  ...requester ? { requester } : {},
@@ -5917,295 +5140,6 @@ function prewarmCodexAttemptClient(params) {
5917
5140
  });
5918
5141
  }
5919
5142
  //#endregion
5920
- //#region extensions/codex/src/app-server/scheduled-app-authority.ts
5921
- const CODEX_SCHEDULED_APP_AUTHORITY_NAMESPACE = "codex.apps";
5922
- const CODEX_APPS_MCP_SERVER = "codex_apps";
5923
- const MCP_STATUS_PAGE_SIZE = 100;
5924
- const MCP_STATUS_MAX_PAGES = 100;
5925
- const CODEX_APP_AUTHORITY_CAPTURE_TIMEOUT_MS = 6e4;
5926
- const CODEX_APP_AUTHORITY_CAPTURE_MIN_TIMEOUT_MS = 100;
5927
- function resolveScheduledCodexAppCreatorCaptureDecision(params) {
5928
- if (!params.appsMayBeVisible) return {
5929
- required: false,
5930
- supported: false
5931
- };
5932
- const unavailableReason = params.authenticatedScheduledMode ? "A scheduled Codex continuation cannot create new app-authorized automations. Recreate it from a fresh authenticated owner turn; no automation changes were saved." : params.usesSupervisionConnection ? "Codex apps are visible through a supervised connection that cannot capture creator authority. Use an isolated prepared-profile Codex creator turn; no automation changes were saved." : params.homeScope === "user" ? "Codex apps are visible through a user-home runtime that cannot capture isolated creator authority. Use an agent-scoped prepared-profile Codex creator turn; no automation changes were saved." : !params.hasPreparedAccountIdentity ? "Codex app authority requires a genuine ChatGPT account identity. Reauthenticate the selected Codex profile, then retry; no automation changes were saved." : void 0;
5933
- return {
5934
- required: true,
5935
- supported: !unavailableReason,
5936
- ...unavailableReason ? { unavailableReason } : {}
5937
- };
5938
- }
5939
- function normalizeApprovalMode(value) {
5940
- return value === "allow" || value === "deny" || value === "auto" || value === "ask" ? value : void 0;
5941
- }
5942
- function normalizeAppToolApprovalMode(value) {
5943
- return value === "auto" || value === "prompt" || value === "writes" || value === "approve" ? value : void 0;
5944
- }
5945
- function defaultApprovalMode(entry) {
5946
- return entry.destructiveApprovalMode ?? (entry.allowDestructiveActions ? "allow" : "deny");
5947
- }
5948
- function parseScheduledCodexAppAuthority(authority) {
5949
- if (!authority || authority.runtimeId !== "codex") return;
5950
- if (authority.version !== 1) throw new Error("Unsupported Codex scheduled authority version; reauthorize this automation.");
5951
- if (authority.namespace !== CODEX_SCHEDULED_APP_AUTHORITY_NAMESPACE) throw new Error(`Unsupported Codex scheduled authority namespace ${authority.namespace}; reauthorize this automation.`);
5952
- const payload = asOptionalRecord(authority.payload);
5953
- const auth = asOptionalRecord(payload?.auth);
5954
- const profileId = normalizeOptionalString(auth?.profileId);
5955
- const accountId = normalizeOptionalString(auth?.accountId);
5956
- if (payload?.version !== 1 || !profileId || !accountId || !Array.isArray(payload.apps)) throw new Error("Stored Codex app authority is invalid; reauthorize this automation.");
5957
- const seen = /* @__PURE__ */ new Set();
5958
- const apps = payload.apps.map((raw) => {
5959
- const app = asOptionalRecord(raw);
5960
- const id = normalizeOptionalString(app?.id);
5961
- const destructiveApprovalMode = normalizeApprovalMode(app?.destructiveApprovalMode);
5962
- const rawTools = asOptionalRecord(app?.tools);
5963
- if (!id || seen.has(id) || typeof app?.allowDestructiveActions !== "boolean" || typeof app.allowOpenWorld !== "boolean" || !destructiveApprovalMode || !rawTools) throw new Error("Stored Codex app authority is invalid; reauthorize this automation.");
5964
- seen.add(id);
5965
- const tools = {};
5966
- for (const [name, rawMode] of Object.entries(rawTools)) {
5967
- const toolName = normalizeOptionalString(name);
5968
- const mode = normalizeAppToolApprovalMode(rawMode);
5969
- if (!toolName || !mode) throw new Error("Stored Codex app authority is invalid; reauthorize this automation.");
5970
- tools[toolName] = mode;
5971
- }
5972
- return {
5973
- id,
5974
- allowDestructiveActions: app.allowDestructiveActions,
5975
- allowOpenWorld: app.allowOpenWorld,
5976
- destructiveApprovalMode,
5977
- tools
5978
- };
5979
- });
5980
- return {
5981
- version: 1,
5982
- auth: {
5983
- profileId,
5984
- accountId
5985
- },
5986
- apps
5987
- };
5988
- }
5989
- async function readCodexScheduledAppToolNamesByApp(params) {
5990
- const toolNamesByApp = /* @__PURE__ */ new Map();
5991
- const seenCursors = /* @__PURE__ */ new Set();
5992
- let cursor;
5993
- for (let page = 0; page < MCP_STATUS_MAX_PAGES; page += 1) {
5994
- const response = await params.request("mcpServerStatus/list", {
5995
- ...params.threadId ? { threadId: params.threadId } : {},
5996
- detail: "toolsAndAuthOnly",
5997
- limit: MCP_STATUS_PAGE_SIZE,
5998
- ...cursor ? { cursor } : {}
5999
- });
6000
- if (!isJsonObject(response) || !Array.isArray(response.data)) throw new Error("Codex mcpServerStatus/list returned invalid scheduled app inventory");
6001
- for (const status of response.data) {
6002
- if (!isJsonObject(status) || !isJsonObject(status.tools)) throw new Error("Codex scheduled app inventory contained an invalid server status");
6003
- if (status.name !== CODEX_APPS_MCP_SERVER) continue;
6004
- for (const [toolName, tool] of Object.entries(status.tools)) {
6005
- const connectorId = readCodexMcpToolConnectorId(tool);
6006
- if (connectorId) {
6007
- const names = toolNamesByApp.get(connectorId) ?? /* @__PURE__ */ new Set();
6008
- names.add(toolName);
6009
- toolNamesByApp.set(connectorId, names);
6010
- }
6011
- }
6012
- }
6013
- if (response.nextCursor !== void 0 && response.nextCursor !== null && typeof response.nextCursor !== "string") throw new Error("Codex scheduled app inventory returned an invalid pagination cursor");
6014
- cursor = response.nextCursor;
6015
- if (!cursor) return toolNamesByApp;
6016
- if (seenCursors.has(cursor)) throw new Error("Codex app connector inventory repeated its pagination cursor");
6017
- seenCursors.add(cursor);
6018
- }
6019
- throw new Error("Codex app connector inventory exceeded its bounded page limit");
6020
- }
6021
- /** Reads the current account policy and connector-backed tool names under one caller deadline. */
6022
- async function readCurrentCodexScheduledAppPolicy$1(params) {
6023
- const [configResponse, toolNamesByApp] = await Promise.all([params.request("config/read", {
6024
- includeLayers: false,
6025
- ...params.configCwd ? { cwd: params.configCwd } : {}
6026
- }), readCodexScheduledAppToolNamesByApp(params)]);
6027
- if (!isJsonObject(configResponse)) throw new Error("Codex config/read returned an invalid scheduled app policy response");
6028
- return {
6029
- config: isJsonObject(configResponse.config) ? configResponse.config : {},
6030
- toolNamesByApp
6031
- };
6032
- }
6033
- function readToolApprovalMode(config, appId, toolName, fallback = "auto") {
6034
- return normalizeAppToolApprovalMode(asOptionalRecord(asOptionalRecord(asOptionalRecord(asOptionalRecord(config.apps)?.[appId])?.tools)?.[toolName])?.approval_mode) ?? fallback;
6035
- }
6036
- /** Captures only apps callable on the exact active Codex client/thread. */
6037
- async function captureScheduledCodexAppAuthority(params) {
6038
- const requestedTimeoutMs = params.timeoutMs ?? CODEX_APP_AUTHORITY_CAPTURE_TIMEOUT_MS;
6039
- const timeoutMs = Math.min(CODEX_APP_AUTHORITY_CAPTURE_TIMEOUT_MS, Math.max(CODEX_APP_AUTHORITY_CAPTURE_MIN_TIMEOUT_MS, Number.isFinite(requestedTimeoutMs) ? Math.floor(requestedTimeoutMs) : CODEX_APP_AUTHORITY_CAPTURE_TIMEOUT_MS));
6040
- const deadlineMs = Date.now() + timeoutMs;
6041
- const boundedClient = { request: ((method, requestParams) => {
6042
- const remainingTimeoutMs = deadlineMs - Date.now();
6043
- if (remainingTimeoutMs <= 0) throw new CodexScheduledAppAuthorityCaptureTimeoutError();
6044
- return params.client.request(method, requestParams, {
6045
- timeoutMs: remainingTimeoutMs,
6046
- signal: params.signal
6047
- });
6048
- }) };
6049
- let installed;
6050
- let currentPolicy;
6051
- try {
6052
- [installed, currentPolicy] = await withAbortableTimeout({
6053
- promise: Promise.all([boundedClient.request("app/installed", {
6054
- threadId: params.threadId,
6055
- forceRefresh: false
6056
- }), readCurrentCodexScheduledAppPolicy$1({
6057
- request: (method, requestParams) => boundedClient.request(method, requestParams),
6058
- threadId: params.threadId,
6059
- configCwd: params.configCwd
6060
- })]),
6061
- timeoutMs,
6062
- signal: params.signal,
6063
- timeoutMessage: "Codex scheduled app authority capture deadline elapsed",
6064
- createTimeoutError: () => new CodexScheduledAppAuthorityCaptureTimeoutError()
6065
- });
6066
- } catch (error) {
6067
- if (params.signal?.aborted || !(error instanceof CodexScheduledAppAuthorityCaptureTimeoutError) && !isCodexAppServerRequestTimeoutError(error)) throw error;
6068
- throw new Error(`Codex app authority capture exceeded its ${timeoutMs} ms total budget. No automation changes were saved; retry after Codex app inventory is responsive.`, { cause: error });
6069
- }
6070
- const callableIds = new Set(installed.apps.filter((app) => app.enabled && app.callable).map((app) => app.id));
6071
- const apps = Object.entries(params.policyContext.apps).filter(([id]) => callableIds.has(id) && currentPolicy.toolNamesByApp.has(id)).map(([id, policy]) => ({
6072
- id,
6073
- allowDestructiveActions: policy.allowDestructiveActions,
6074
- allowOpenWorld: policy.allowOpenWorld !== false,
6075
- destructiveApprovalMode: defaultApprovalMode(policy),
6076
- tools: Object.fromEntries([...currentPolicy.toolNamesByApp.get(id) ?? []].toSorted().map((toolName) => [toolName, readToolApprovalMode(currentPolicy.config, id, toolName, appApprovalCeiling(defaultApprovalMode(policy)))]))
6077
- })).toSorted((left, right) => left.id.localeCompare(right.id));
6078
- if (apps.length === 0) return;
6079
- return {
6080
- version: 1,
6081
- runtimeId: "codex",
6082
- namespace: CODEX_SCHEDULED_APP_AUTHORITY_NAMESPACE,
6083
- payload: {
6084
- version: 1,
6085
- auth: {
6086
- profileId: params.profileId,
6087
- accountId: params.accountId
6088
- },
6089
- apps
6090
- }
6091
- };
6092
- }
6093
- var CodexScheduledAppAuthorityCaptureTimeoutError = class extends Error {
6094
- constructor() {
6095
- super("Codex scheduled app authority capture deadline elapsed");
6096
- this.name = "CodexScheduledAppAuthorityCaptureTimeoutError";
6097
- }
6098
- };
6099
- const APPROVAL_RANK = {
6100
- deny: 0,
6101
- ask: 1,
6102
- auto: 2,
6103
- allow: 3
6104
- };
6105
- function stricterApprovalMode(left, right) {
6106
- return APPROVAL_RANK[left] <= APPROVAL_RANK[right] ? left : right;
6107
- }
6108
- function intersectToolApprovalMode(captured, current) {
6109
- if (captured === current) return captured;
6110
- if (captured === "prompt" || current === "prompt") return "prompt";
6111
- if (captured === "approve") return current;
6112
- if (current === "approve") return captured;
6113
- return "prompt";
6114
- }
6115
- function appApprovalCeiling(mode) {
6116
- if (mode === "allow") return "approve";
6117
- return mode === "ask" ? "prompt" : "auto";
6118
- }
6119
- function stableStringify(value) {
6120
- if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
6121
- if (value && typeof value === "object") return `{${Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`).join(",")}}`;
6122
- return JSON.stringify(value);
6123
- }
6124
- /** Intersects a stored app-ID cap with current policy without admitting new apps. */
6125
- function intersectCodexPluginThreadConfigWithScheduledAuthority(config, authority, currentPolicy = {
6126
- config: {},
6127
- toolNamesByApp: /* @__PURE__ */ new Map()
6128
- }) {
6129
- const scheduled = parseScheduledCodexAppAuthority(authority);
6130
- if (!scheduled) return config;
6131
- const omittedAppIds = scheduled.apps.map((app) => app.id).filter((id) => {
6132
- const currentTools = currentPolicy.toolNamesByApp.get(id);
6133
- return !Object.hasOwn(config.policyContext.apps, id) || !currentTools || currentTools.size === 0;
6134
- }).toSorted();
6135
- if (omittedAppIds.length > 0) {
6136
- const visibleIds = omittedAppIds.slice(0, 10).join(", ");
6137
- const remaining = omittedAppIds.length - Math.min(omittedAppIds.length, 10);
6138
- throw new AgentHarnessPreflightError(`Scheduled Codex apps are unavailable under the current policy or account: ${visibleIds}${remaining > 0 ? ` (and ${remaining} more)` : ""}. Restore access or reauthorize the automation from a fresh authenticated Codex owner turn.`);
6139
- }
6140
- const capturedById = new Map(scheduled.apps.map((app) => [app.id, app]));
6141
- const apps = {};
6142
- for (const [id, current] of Object.entries(config.policyContext.apps)) {
6143
- const captured = capturedById.get(id);
6144
- if (!captured) continue;
6145
- apps[id] = {
6146
- ...current,
6147
- allowDestructiveActions: current.allowDestructiveActions && captured.allowDestructiveActions,
6148
- allowOpenWorld: current.allowOpenWorld !== false && captured.allowOpenWorld,
6149
- destructiveApprovalMode: stricterApprovalMode(defaultApprovalMode(current), captured.destructiveApprovalMode)
6150
- };
6151
- }
6152
- const policyContext = buildPluginAppPolicyContext(apps, Object.fromEntries(Object.entries(config.policyContext.pluginAppIds).map(([key, ids]) => [key, ids.filter((id) => Object.hasOwn(apps, id))]).filter(([, ids]) => ids.length > 0)));
6153
- const configPatch = buildCodexPluginAppsConfigPatchFromPolicyContext(policyContext);
6154
- const appsPatch = asOptionalRecord(configPatch.apps);
6155
- for (const [appId, captured] of capturedById) {
6156
- const appPatch = asOptionalRecord(appsPatch?.[appId]);
6157
- if (!appPatch || !Object.hasOwn(apps, appId)) continue;
6158
- const currentApp = apps[appId];
6159
- if (!currentApp) continue;
6160
- const storedAppCeiling = appApprovalCeiling(captured.destructiveApprovalMode);
6161
- const currentAppCeiling = appApprovalCeiling(defaultApprovalMode(currentApp));
6162
- const toolNames = currentPolicy.toolNamesByApp.get(appId) ?? /* @__PURE__ */ new Set();
6163
- appPatch.tools = Object.fromEntries([...toolNames].toSorted().map((toolName) => {
6164
- return [toolName, { approval_mode: intersectToolApprovalMode(intersectToolApprovalMode(captured.tools[toolName] ?? storedAppCeiling, storedAppCeiling), intersectToolApprovalMode(readToolApprovalMode(currentPolicy.config, appId, toolName, currentAppCeiling), currentAppCeiling)) }];
6165
- }));
6166
- }
6167
- const fingerprint = crypto.createHash("sha256").update(stableStringify({
6168
- version: 1,
6169
- namespace: CODEX_SCHEDULED_APP_AUTHORITY_NAMESPACE,
6170
- authority: scheduled,
6171
- inputFingerprint: config.inputFingerprint,
6172
- policyContext,
6173
- configPatch
6174
- })).digest("hex");
6175
- return {
6176
- ...config,
6177
- fingerprint,
6178
- configPatch,
6179
- provisionalAppIds: Object.keys(apps).toSorted(),
6180
- policyContext
6181
- };
6182
- }
6183
- function readScheduledCodexAppAuthorityAuth(authority) {
6184
- return parseScheduledCodexAppAuthority(authority)?.auth;
6185
- }
6186
- function assertScheduledCodexAppAuthorityRuntime(connection, params) {
6187
- const scheduledAuth = readScheduledCodexAppAuthorityAuth(params.scheduledRuntimeAuthority);
6188
- if (!scheduledAuth) return;
6189
- if (params.trigger !== "cron" || connection.usesSupervisionConnection || connection.appServer.start.homeScope === "user") throw new AgentHarnessPreflightError("This automation's Codex app authority requires an isolated scheduled prepared-profile runtime. Reauthorize it from a supported Codex creator turn.");
6190
- const prepared = connection.startupPreparedAuth;
6191
- if (prepared?.kind !== "profile" || prepared.profileId !== scheduledAuth.profileId || prepared.snapshot?.loginParams.type !== "chatgptAuthTokens" || prepared.snapshot.chatgptAccountId !== scheduledAuth.accountId) throw new AgentHarnessPreflightError(`This automation was authorized for Codex profile ${scheduledAuth.profileId}, but that exact prepared account is not active. Restore the profile or reauthorize the automation from a fresh owner turn.`);
6192
- }
6193
- function buildLegacyScheduledCodexAppRecoveryPrompt(params) {
6194
- if (params.trigger !== "cron" || !params.scheduledRuntimeAuthorityRecoveryRequired || params.scheduledRuntimeAuthority) return;
6195
- return "Scheduled Codex app access is unavailable because this automation predates runtime-specific app authority capture. Tell the operator to recreate or reauthorize it from a fresh authenticated Codex owner turn; do not claim an app action succeeded.";
6196
- }
6197
- /** Makes stored-cap identity part of thread reuse admission, including cap removal. */
6198
- function buildScheduledCodexAppAuthorityInputFingerprint(baseFingerprint, authority) {
6199
- const scheduled = parseScheduledCodexAppAuthority(authority);
6200
- if (!scheduled) return baseFingerprint;
6201
- return crypto.createHash("sha256").update(stableStringify({
6202
- version: 1,
6203
- namespace: CODEX_SCHEDULED_APP_AUTHORITY_NAMESPACE,
6204
- baseFingerprint,
6205
- authority: scheduled
6206
- })).digest("hex");
6207
- }
6208
- //#endregion
6209
5143
  //#region extensions/codex/src/app-server/scheduled-configured-mcp-authority.ts
6210
5144
  /** Limits fresh scheduled-authority capture to authenticated local durable operator turns. */
6211
5145
  function canResolveScheduledConfiguredMcpCreatorAuthority(params) {
@@ -6218,7 +5152,7 @@ function resolveCodexAttemptBundleManifestRegistry(preparedModelRuntime) {
6218
5152
  return metadataSnapshot?.pluginIds === void 0 ? metadataSnapshot?.manifestRegistry : void 0;
6219
5153
  }
6220
5154
  async function prepareCodexAttemptRuntime(connection) {
6221
- const { params, pluginConfig, usesSupervisionConnection, appServer, startupAuthProfileId, startupPreparedAuth, startupClientAuthProfileId, agentDir, preDynamicStartupStages, effectiveWorkspace, contextSessionKey, sandboxSessionKey, sessionAgentId, sandbox, attemptClientFactory, runAbortController, activeContextEngine, mutable } = connection;
5155
+ const { params, pluginConfig, usesSupervisionConnection, appServer, startupAuthProfileId, startupPreparedAuth, startupClientAuthProfileId, agentDir, preDynamicStartupStages, effectiveWorkspace, contextSessionKey, sandboxSessionKey, sessionAgentId, policyAgentId, sandbox, attemptClientFactory, runAbortController, activeContextEngine, mutable } = connection;
6222
5156
  const preparedAuthBinding = !usesSupervisionConnection && appServer.start.homeScope !== "user" && startupAuthProfileId ? await prepareCodexAppServerAuthBinding({
6223
5157
  authProfileId: startupAuthProfileId,
6224
5158
  authProfileStore: params.authProfileStore,
@@ -6291,6 +5225,7 @@ async function prepareCodexAttemptRuntime(connection) {
6291
5225
  const bundleManifestRegistry = resolveCodexAttemptBundleManifestRegistry(params.preparedModelRuntime);
6292
5226
  const bundleMcpThreadConfig = await loadCodexBundleMcpThreadConfig({
6293
5227
  workspaceDir: effectiveWorkspace,
5228
+ agentId: sessionAgentId,
6294
5229
  cfg: params.config,
6295
5230
  toolsEnabled: usesSupervisionConnection || supportsModelTools(params.model),
6296
5231
  disableTools: params.disableTools,
@@ -6299,7 +5234,7 @@ async function prepareCodexAttemptRuntime(connection) {
6299
5234
  toolOverrides: codexMcpToolOverrides
6300
5235
  });
6301
5236
  const authenticatedScheduledMode = params.trigger === "cron" && params.scheduledToolPolicy !== void 0 && Array.isArray(params.toolsAllow);
6302
- const ownsScheduledConfiguredMcpSurface = authenticatedScheduledMode && (bundleMcpThreadConfig.staticServerNames.length > 0 || mutable.startupBinding?.configuredMcpOwnershipVersion === 1);
5237
+ const scheduledConfiguredMcpSurface = authenticatedScheduledMode && (bundleMcpThreadConfig.staticServerNames.length > 0 || mutable.startupBinding?.configuredMcpOwnershipVersion === 1);
6303
5238
  const cronCreatorAuthorityCapability = params.cronCreatorAuthorityCapability;
6304
5239
  const hasFreshCreatorAuthority = cronCreatorAuthorityCapability?.active === true && cronCreatorAuthorityCapability.runId === params.runId && !cronCreatorAuthorityCapability.signal.aborted;
6305
5240
  const mayResolveScheduledConfiguredMcpCreatorAuthority = !authenticatedScheduledMode && canResolveScheduledConfiguredMcpCreatorAuthority({
@@ -6322,10 +5257,11 @@ async function prepareCodexAttemptRuntime(connection) {
6322
5257
  preDynamicStartupStages.mark("bundle-mcp");
6323
5258
  const sandboxExecServerEnabled = isCodexSandboxExecServerEnabled(pluginConfig, sandbox);
6324
5259
  const nativeToolSurfaceEnabled = shouldEnableCodexAppServerNativeToolSurface(runtimeParams, sandbox, {
6325
- agentId: sessionAgentId,
5260
+ agentId: policyAgentId,
6326
5261
  runtimeSessionKey: sandboxSessionKey,
6327
5262
  sandboxExecServerEnabled
6328
5263
  });
5264
+ const configuredMcpSurface = scheduledConfiguredMcpSurface ? "scheduled" : !nativeToolSurfaceEnabled && bundleMcpThreadConfig.staticServerNames.length > 0 ? "transient" : void 0;
6329
5265
  preDynamicStartupStages.mark("native-tool-surface");
6330
5266
  const nativeProviderWebSearchSupport = resolveCodexWebSearchPlan({
6331
5267
  config: params.config,
@@ -6375,7 +5311,7 @@ async function prepareCodexAttemptRuntime(connection) {
6375
5311
  bundleMcpThreadConfig,
6376
5312
  bundleManifestRegistry,
6377
5313
  authenticatedScheduledMode,
6378
- ownsScheduledConfiguredMcpSurface,
5314
+ configuredMcpSurface,
6379
5315
  canResolveScheduledConfiguredMcpCreatorAuthority: mayResolveScheduledConfiguredMcpCreatorAuthority,
6380
5316
  codexMcpToolOverrides,
6381
5317
  sandboxExecServerEnabled,
@@ -6469,7 +5405,6 @@ function resolveCodexDynamicToolDirectNames(params, hostSystemAgentActive = fals
6469
5405
  const names = [];
6470
5406
  if (hostSystemAgentActive && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow)) names.push("openclaw");
6471
5407
  if (params.sourceReplyDeliveryMode === "message_tool_only") names.push("message");
6472
- names.push("progress_card");
6473
5408
  return names;
6474
5409
  }
6475
5410
  //#endregion
@@ -6485,37 +5420,32 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6485
5420
  const { runtime, attemptTools } = context;
6486
5421
  const { connection } = runtime;
6487
5422
  const { params, computerUseConfig, runAbortController, appServer, sessionAgentId } = connection;
5423
+ const autoApprove = shouldAutoApproveCodexAppServerApprovals(appServer);
6488
5424
  const { compactionPlanState, toolBridge, toolOutcomeOrdinals, suppressedDynamicToolOutcomeOrdinals, allocateCodexToolOutcomeOrdinal } = attemptTools;
6489
- const { state, turnIdRef, userInputBridgeRef, openClawDynamicToolExecutions, pendingOpenClawDynamicToolCompletionIds, postToolRawAssistantCompletionIdleTimeoutMs, turnWatches } = turnRuntime;
5425
+ const { state, turnIdRef, userInputBridgeRef, openClawDynamicToolExecutions, pendingOpenClawDynamicToolCompletionIds, noteProgress } = turnRuntime;
6490
5426
  const { emitExecutionPhaseOnce, scheduleTurnReleaseAfterTerminalDynamicTool, scheduleTerminalDynamicToolReleaseCheck } = lifecycle;
6491
5427
  const handleServerRequest = async (request, scope, requestSignal = new AbortController().signal) => {
6492
5428
  const signal = AbortSignal.any([runAbortController.signal, requestSignal]);
6493
5429
  const turnId = turnIdRef.current;
6494
5430
  const projector = projectorRef.current;
6495
- let armCompletionWatchOnResponse = false;
6496
5431
  let requestCountsAsTurnActivity = false;
6497
- let requestKeepsAttemptWatchArmed = false;
6498
- const markCurrentTurnRequestProgress = (options) => {
5432
+ const markCurrentTurnRequestProgress = () => {
6499
5433
  state.activeAppServerTurnRequests += 1;
6500
- requestKeepsAttemptWatchArmed = options?.hasIndependentTimeout !== true;
6501
- if (requestKeepsAttemptWatchArmed) state.activeAppServerTurnRequestsWithoutTimeout += 1;
6502
- turnWatches.clearCompletionIdleTimer();
6503
- turnWatches.disarmAssistantCompletionIdleWatch();
6504
5434
  requestCountsAsTurnActivity = true;
6505
- turnWatches.touchActivity(`request:${request.method}:start`, { attemptProgress: true });
5435
+ noteProgress(`request:${request.method}:start`);
6506
5436
  };
6507
5437
  try {
6508
5438
  if (!turnId) return;
6509
5439
  if (request.method === "mcpServer/elicitation/request") {
6510
- if (!scope.turnId || scope.turnId === turnId) {
6511
- armCompletionWatchOnResponse = true;
6512
- markCurrentTurnRequestProgress();
6513
- }
5440
+ if (!scope.turnId || scope.turnId === turnId) markCurrentTurnRequestProgress();
6514
5441
  const approvalResult = await routeCodexAppServerElicitationRequest({
6515
5442
  requestParams: request.params,
6516
5443
  paramsForRun: params,
6517
5444
  threadId: resourceState.thread.threadId,
6518
5445
  turnId,
5446
+ autoApproveMcpTools: autoApprove,
5447
+ projectedMcpServers: runtime.bundleMcpThreadConfig.configPatch?.mcp_servers,
5448
+ getActiveMcpToolCall: (serverName) => projector?.getActiveMcpToolCall(serverName),
6519
5449
  pluginAppPolicyContext: resourceState.thread.pluginAppPolicyContext,
6520
5450
  ...computerUseConfig.enabled ? { computerUseMcpServerName: computerUseConfig.mcpServerName } : {},
6521
5451
  signal
@@ -6527,10 +5457,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6527
5457
  });
6528
5458
  }
6529
5459
  if (request.method === "item/tool/requestUserInput") {
6530
- if (scope.turnId === turnId) {
6531
- armCompletionWatchOnResponse = true;
6532
- markCurrentTurnRequestProgress();
6533
- }
5460
+ if (scope.turnId === turnId) markCurrentTurnRequestProgress();
6534
5461
  return await userInputBridgeRef.current?.handleRequest({
6535
5462
  id: request.id,
6536
5463
  params: request.params
@@ -6538,10 +5465,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6538
5465
  }
6539
5466
  if (request.method !== "item/tool/call") {
6540
5467
  if (isCodexAppServerApprovalRequest(request.method)) {
6541
- if (scope.turnId === turnId) {
6542
- armCompletionWatchOnResponse = true;
6543
- markCurrentTurnRequestProgress();
6544
- }
5468
+ if (scope.turnId === turnId) markCurrentTurnRequestProgress();
6545
5469
  return await handleCodexAppServerApprovalRequest({
6546
5470
  method: request.method,
6547
5471
  requestParams: request.params,
@@ -6549,7 +5473,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6549
5473
  threadId: resourceState.thread.threadId,
6550
5474
  turnId,
6551
5475
  nativeHookRelay: resourceState.nativeHookRelay,
6552
- autoApprove: shouldAutoApproveCodexAppServerApprovals(appServer),
5476
+ autoApprove,
6553
5477
  signal,
6554
5478
  onNativeToolFailureDisposition: (itemId, disposition, approvalKind) => projector?.recordNativeToolApprovalFailure(itemId, disposition, approvalKind)
6555
5479
  });
@@ -6560,15 +5484,11 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6560
5484
  if (!call || call.threadId !== resourceState.thread.threadId || call.turnId !== turnId) return;
6561
5485
  const replayedExecution = openClawDynamicToolExecutions.get(call);
6562
5486
  if (replayedExecution) {
6563
- armCompletionWatchOnResponse = true;
6564
- markCurrentTurnRequestProgress({ hasIndependentTimeout: true });
6565
- state.turnCrossedToolHandoff = true;
5487
+ markCurrentTurnRequestProgress();
6566
5488
  return toCodexDynamicToolProtocolResponse(await replayedExecution);
6567
5489
  }
6568
5490
  const toolCallOrdinal = allocateCodexToolOutcomeOrdinal?.(call.callId);
6569
- armCompletionWatchOnResponse = true;
6570
- markCurrentTurnRequestProgress({ hasIndependentTimeout: true });
6571
- state.turnCrossedToolHandoff = true;
5491
+ markCurrentTurnRequestProgress();
6572
5492
  pendingOpenClawDynamicToolCompletionIds.add(call.callId);
6573
5493
  trajectoryRecorder?.recordEvent("tool.call", {
6574
5494
  threadId: call.threadId,
@@ -6596,6 +5516,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6596
5516
  data: {
6597
5517
  phase: "start",
6598
5518
  name: call.tool,
5519
+ itemId: call.callId,
6599
5520
  toolCallId: call.callId,
6600
5521
  ...toolMeta ? { meta: toolMeta } : {},
6601
5522
  ...toolArgs ? { args: toolArgs } : {},
@@ -6618,7 +5539,8 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6618
5539
  })) terminalDiagnosticObserved = true;
6619
5540
  }, { include: DYNAMIC_TOOL_TERMINAL_DIAGNOSTIC_TYPES });
6620
5541
  try {
6621
- const { execution } = openClawDynamicToolExecutions.claim(call, () => {
5542
+ const { execution } = openClawDynamicToolExecutions.claim(call, async () => {
5543
+ await projector?.transcriptCheckpoint.flush();
6622
5544
  emitDynamicToolStartedDiagnostic({
6623
5545
  call,
6624
5546
  agentId: sessionAgentId,
@@ -6626,7 +5548,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6626
5548
  sessionId: params.sessionId,
6627
5549
  sessionKey: params.sessionKey
6628
5550
  });
6629
- return handleDynamicToolCallWithTimeout({
5551
+ const response = await handleDynamicToolCallWithTimeout({
6630
5552
  call,
6631
5553
  toolBridge,
6632
5554
  signal,
@@ -6648,6 +5570,9 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6648
5570
  });
6649
5571
  }
6650
5572
  });
5573
+ recordCodexDynamicToolResult(projector, call, response, toCodexDynamicToolProtocolResponse(response));
5574
+ await projector?.transcriptCheckpoint.flush();
5575
+ return response;
6651
5576
  });
6652
5577
  const response = await execution;
6653
5578
  const protocolResponse = toCodexDynamicToolProtocolResponse(response);
@@ -6671,7 +5596,6 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6671
5596
  success: protocolResponse.success,
6672
5597
  contentItems: protocolResponse.contentItems
6673
5598
  });
6674
- recordCodexDynamicToolResult(projector, call, response, protocolResponse);
6675
5599
  if (protocolResponse.success && call.tool === "progress_card") {
6676
5600
  const progressCardInput = response.executedArguments ?? call.arguments;
6677
5601
  await projector?.recordDynamicProgressCardUpdate(progressCardInput);
@@ -6684,6 +5608,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6684
5608
  data: {
6685
5609
  phase: "result",
6686
5610
  name: call.tool,
5611
+ itemId: call.callId,
6687
5612
  toolCallId: call.callId,
6688
5613
  ...toolMeta ? { meta: toolMeta } : {},
6689
5614
  ...commandBearing ? { commandBearing: true } : {},
@@ -6740,17 +5665,10 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6740
5665
  }
6741
5666
  } finally {
6742
5667
  if (requestCountsAsTurnActivity) {
6743
- state.activeAppServerTurnRequests = Math.max(0, state.activeAppServerTurnRequests - 1);
6744
- if (requestKeepsAttemptWatchArmed) state.activeAppServerTurnRequestsWithoutTimeout = Math.max(0, state.activeAppServerTurnRequestsWithoutTimeout - 1);
6745
- const postToolContinuationTimeoutMs = request.method === "item/tool/call" && state.turnCrossedToolHandoff ? postToolRawAssistantCompletionIdleTimeoutMs : void 0;
6746
- turnWatches.touchActivity(`request:${request.method}:response`, {
6747
- arm: armCompletionWatchOnResponse,
6748
- attemptProgress: true,
6749
- ...postToolContinuationTimeoutMs !== void 0 ? { attemptTimeoutMs: postToolContinuationTimeoutMs } : {}
6750
- });
6751
- if (armCompletionWatchOnResponse && postToolContinuationTimeoutMs !== void 0) turnWatches.armCompletionIdleWatch({ timeoutMs: postToolContinuationTimeoutMs });
5668
+ state.activeAppServerTurnRequests -= 1;
5669
+ noteProgress(`request:${request.method}:response`);
6752
5670
  scheduleTerminalDynamicToolReleaseCheck();
6753
- } else turnWatches.scheduleProgressWatches();
5671
+ }
6754
5672
  }
6755
5673
  };
6756
5674
  return { handleServerRequest };
@@ -6759,7 +5677,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
6759
5677
  //#region extensions/codex/src/app-server/attempt-runtime-artifact.ts
6760
5678
  async function verifyStartupArtifact(params) {
6761
5679
  if (!params.request) return;
6762
- const { readCodexAppServerClientRuntimeArtifact, validateCodexAppServerRuntimeArtifact } = await import("./runtime-artifact-BvyT1uyd.js");
5680
+ const { readCodexAppServerClientRuntimeArtifact, validateCodexAppServerRuntimeArtifact } = await import("./runtime-artifact-C3jVimFn.js");
6763
5681
  const runtimeArtifact = readCodexAppServerClientRuntimeArtifact(params.client);
6764
5682
  const expected = params.request.expected;
6765
5683
  const matchesExpected = !expected || runtimeArtifact?.id === expected.id && runtimeArtifact.fingerprint === expected.fingerprint;
@@ -7006,14 +5924,13 @@ async function startCodexAttemptThread(params) {
7006
5924
  startupClientForAbandonedRequestCleanup = void 0;
7007
5925
  },
7008
5926
  operation: async () => {
7009
- const threadConfig = mergeCodexThreadConfigs(params.configuredMcpOwnershipVersion === 1 ? void 0 : params.bundleMcpThreadConfig?.configPatch);
5927
+ const threadConfig = mergeCodexThreadConfigs(params.configuredMcpDynamicSurface ? void 0 : params.bundleMcpThreadConfig?.configPatch);
7010
5928
  const { pluginThreadConfigRequired, pluginThreadConfigPluginConfig, resolvedPluginPolicy, enabledPluginConfigKeys } = resolveCodexPluginThreadConfigStartupPolicy({
7011
5929
  pluginConfig: params.pluginConfig,
7012
5930
  nativeToolSurfaceEnabled: params.nativeToolSurfaceEnabled,
7013
5931
  scheduledRuntimeAuthority: params.buildAttemptParams().scheduledRuntimeAuthority
7014
5932
  });
7015
- const computerUseMcpElicitationDelegationRequired = params.computerUseConfig.enabled;
7016
- pluginAppServer = resolvedPluginPolicy?.enabled === true || computerUseMcpElicitationDelegationRequired ? {
5933
+ pluginAppServer = resolvedPluginPolicy?.enabled === true || params.computerUseConfig.enabled || params.nativeToolSurfaceEnabled && params.configuredMcpOwnershipVersion !== 1 && hasCodexMcpToolApprovalOverrides(params.config?.mcp?.servers, params.bundleMcpThreadConfig.userStaticServerNames, params.bundleMcpThreadConfig.configPatch?.mcp_servers) ? {
7017
5934
  ...params.appServer,
7018
5935
  approvalPolicy: withMcpElicitationsApprovalPolicy(params.appServer.approvalPolicy)
7019
5936
  } : params.appServer;
@@ -7205,9 +6122,9 @@ async function startCodexAttemptThread(params) {
7205
6122
  nativeCodeModeEnabled: params.nativeToolSurfaceEnabled,
7206
6123
  nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
7207
6124
  nativeCodeModeOnlyEnabled: params.appServer.codeModeOnly,
7208
- userMcpServersEnabled: params.configuredMcpOwnershipVersion === 1 ? false : params.nativeToolSurfaceEnabled,
7209
- mcpServersFingerprint: params.configuredMcpOwnershipVersion === 1 ? void 0 : params.bundleMcpThreadConfig.fingerprint,
7210
- mcpServersFingerprintEvaluated: params.configuredMcpOwnershipVersion === 1 || params.bundleMcpThreadConfig.evaluated,
6125
+ userMcpServersEnabled: params.configuredMcpDynamicSurface ? false : params.nativeToolSurfaceEnabled,
6126
+ mcpServersFingerprint: params.configuredMcpDynamicSurface ? void 0 : params.bundleMcpThreadConfig.fingerprint,
6127
+ mcpServersFingerprintEvaluated: params.configuredMcpDynamicSurface || params.bundleMcpThreadConfig.evaluated,
7211
6128
  configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
7212
6129
  environmentSelection: startupEnvironmentSelection,
7213
6130
  appServerRuntimeFingerprint,
@@ -7286,10 +6203,7 @@ async function startCodexAttemptThread(params) {
7286
6203
  if (startupAbandoned || params.signal.aborted) {
7287
6204
  if (startupClientForAbandonedRequestCleanup === startupClient) startupClientForAbandonedRequestCleanup = void 0;
7288
6205
  await closeCodexStartupClientBestEffort(startupClient);
7289
- } else if (!isCodexAppServerStartSelectionChangedError(startupAttemptError) && (shouldClearSharedClientAfterStartupRace(startupAttemptError) || shouldClearSharedClientAfterStartupFailure({
7290
- error: startupAttemptError,
7291
- spawnedBy: params.spawnedBy
7292
- }))) {
6206
+ } else if (!isCodexAppServerStartSelectionChangedError(startupAttemptError) && (shouldClearSharedClientAfterStartupRace(startupAttemptError) || shouldClearSharedClientAfterStartupFailure(startupAttemptError, params.spawnedBy))) {
7293
6207
  if (startupClientForAbandonedRequestCleanup === startupClient) startupClientForAbandonedRequestCleanup = void 0;
7294
6208
  await closeCodexStartupClientBestEffort(startupClient);
7295
6209
  }
@@ -7333,15 +6247,12 @@ async function startCodexAttemptThread(params) {
7333
6247
  releaseSharedClientLease
7334
6248
  };
7335
6249
  } catch (error) {
7336
- if (params.signal.aborted || shouldClearSharedClientAfterStartupAbandon(error)) {
6250
+ if (params.signal.aborted || isCodexAppServerStartupError(error)) {
7337
6251
  releaseSharedClientLease?.();
7338
6252
  releaseSharedClientLease = void 0;
7339
6253
  await closeCodexStartupClientBestEffort(startupClientForAbandonedRequestCleanup);
7340
6254
  startupClientForAbandonedRequestCleanup = void 0;
7341
- } else if (!isCodexAppServerStartSelectionChangedError(error) && (shouldClearSharedClientAfterStartupRace(error) || shouldClearSharedClientAfterStartupFailure({
7342
- error,
7343
- spawnedBy: params.spawnedBy
7344
- }))) {
6255
+ } else if (!isCodexAppServerStartSelectionChangedError(error) && (shouldClearSharedClientAfterStartupRace(error) || shouldClearSharedClientAfterStartupFailure(error, params.spawnedBy))) {
7345
6256
  releaseSharedClientLease?.();
7346
6257
  releaseSharedClientLease = void 0;
7347
6258
  await closeCodexStartupClientBestEffort(startupClientForAbandonedRequestCleanup);
@@ -7352,14 +6263,11 @@ async function startCodexAttemptThread(params) {
7352
6263
  params.signal.removeEventListener("abort", abandonStartupAcquire);
7353
6264
  }
7354
6265
  }
7355
- function shouldClearSharedClientAfterStartupAbandon(error) {
7356
- return isCodexAppServerStartupError(error);
7357
- }
7358
6266
  function shouldClearSharedClientAfterStartupRace(error) {
7359
- return shouldClearSharedClientAfterStartupAbandon(error) || isCodexAppServerRequestTimeoutError(error);
6267
+ return isCodexAppServerStartupError(error) || isCodexAppServerRequestTimeoutError(error);
7360
6268
  }
7361
- function shouldClearSharedClientAfterStartupFailure(params) {
7362
- return isCodexAppServerBrokenPipeError(params.error) || !params.spawnedBy;
6269
+ function shouldClearSharedClientAfterStartupFailure(error, spawnedBy) {
6270
+ return !isCodexAppServerOverloadError(error) && !(error instanceof AgentHarnessPreflightError && error.scope === void 0) && (isCodexAppServerBrokenPipeError(error) || !spawnedBy);
7363
6271
  }
7364
6272
  //#endregion
7365
6273
  //#region extensions/codex/src/app-server/run-attempt-start.ts
@@ -7369,7 +6277,7 @@ async function startCodexAttemptRuntime(resources) {
7369
6277
  const { runtime, attemptTools, promptState } = context;
7370
6278
  const { connection, runtimeParams, preparedAuthBinding, buildActiveRunAttemptParams, startupAuthAccountCacheKey, startupEnvApiKeyCacheKey, bundleMcpThreadConfig, nativeToolSurfaceEnabled, nativeProviderWebSearchSupport, effectiveRuntimeModelId, sandboxExecServerEnabled } = runtime;
7371
6279
  const { toolBridge, toolState } = attemptTools;
7372
- const developerInstructions = joinPresentSections(turnState.promptBuild.developerInstructions, attemptTools.scheduledConfiguredMcp?.diagnosticNotice);
6280
+ const developerInstructions = joinPresentSections(turnState.promptBuild.developerInstructions, attemptTools.configuredMcp?.diagnosticNotice);
7373
6281
  const { params, attemptClientFactory, bindingStore, appServer, pluginConfig, computerUseConfig, startupClientAuthProfileId, runtimeArtifactRequest, startupPreparedAuth, agentDir, sessionAgentId, effectiveWorkspace, effectiveCwd, sandbox, runAbortController, usesSupervisionConnection, resolveReviewerPolicyContext, resolveRuntimeOptionsForCurrentBinding, startupAuthProfileId, startupAuthRequirement, abortFromUpstream } = connection;
7374
6282
  let pluginAppServer = withCodexAppServerFastModeServiceTier(appServer, runtimeParams);
7375
6283
  const loopDetectionEnabled = (sessionAgentId && params.config ? resolveAgentConfig$1(params.config, sessionAgentId)?.tools?.loopDetection?.enabled : void 0) ?? params.config?.tools?.loopDetection?.enabled ?? false;
@@ -7409,6 +6317,7 @@ async function startCodexAttemptRuntime(resources) {
7409
6317
  buildFinalConfigPatch: buildNativeHookRelayFinalConfigPatch,
7410
6318
  nativeHookRelayRequired: connection.options.nativeHookRelay?.enabled !== false && params.pluginHarnessToolPolicyRestricted !== true && connection.nativeHookRelayEvents.includes("pre_tool_use") && (hasBeforeToolCallPolicy() || appServer.loopDetectionPreToolUseRelay && Boolean(connection.sandboxSessionKey) && loopDetectionEnabled),
7411
6319
  bundleMcpThreadConfig,
6320
+ configuredMcpDynamicSurface: attemptTools.configuredMcp !== void 0,
7412
6321
  configuredMcpOwnershipVersion: attemptTools.configuredMcpOwnershipVersion,
7413
6322
  nativeToolSurfaceEnabled,
7414
6323
  nativeProviderWebSearchSupport,
@@ -7445,11 +6354,12 @@ async function startCodexAttemptRuntime(resources) {
7445
6354
  if (usesSupervisionConnection && (state.thread.connectionScope !== "supervision" || state.thread.supervisionSourceThreadId !== connection.mutable.startupBinding?.supervisionSourceThreadId)) throw new Error("Codex supervised thread lost its private connection ownership");
7446
6355
  if (state.thread.lifecycle.action === "started" || state.thread.lifecycle.action === "forked") {
7447
6356
  const activePolicy = resolveReviewerPolicyContext(state.thread);
6357
+ const activeConfig = resolveRuntimeOptionsForCurrentBinding({
6358
+ modelProvider: activePolicy.modelProvider,
6359
+ model: activePolicy.model
6360
+ });
7448
6361
  const activeAppServer = resolveCodexAppServerForModelProvider({
7449
- appServer: resolveRuntimeOptionsForCurrentBinding({
7450
- modelProvider: activePolicy.modelProvider,
7451
- model: activePolicy.model
7452
- }),
6362
+ appServer: activeConfig,
7453
6363
  provider: activePolicy.modelProvider,
7454
6364
  model: activePolicy.model,
7455
6365
  config: params.config,
@@ -7490,7 +6400,7 @@ async function startCodexAttemptRuntime(resources) {
7490
6400
  recordCodexTrajectoryContext(trajectoryRecorder, {
7491
6401
  attempt: params,
7492
6402
  cwd: effectiveCwd,
7493
- developerInstructions: joinPresentSections(buildRenderedCodexDeveloperInstructions(), attemptTools.scheduledConfiguredMcp?.diagnosticNotice),
6403
+ developerInstructions: joinPresentSections(buildRenderedCodexDeveloperInstructions(), attemptTools.configuredMcp?.diagnosticNotice),
7494
6404
  prompt: turnState.codexTurnPromptText,
7495
6405
  tools: toolBridge.availableSpecs
7496
6406
  });
@@ -7504,7 +6414,7 @@ async function startCodexAttemptRuntime(resources) {
7504
6414
  await runCleanupStep("codex-start-failure-sandbox-release", releaseSandboxExecEnvironment);
7505
6415
  await runCleanupStep("codex-start-failure-shared-client-release", releaseSharedClientLeaseAndRetireOneShotClient);
7506
6416
  await runCleanupStep("codex-start-failure-abort-listener", () => params.abortSignal?.removeEventListener("abort", abortFromUpstream));
7507
- throw state.executionDisconnectError ?? error;
6417
+ throw error instanceof CodexThreadPolicyHandoffError ? error : state.executionDisconnectError ?? error;
7508
6418
  }
7509
6419
  }
7510
6420
  //#endregion
@@ -7513,8 +6423,8 @@ function isAuthorityResolutionOperationAbort(error, signal) {
7513
6423
  return signal?.aborted === true && error === signal.reason;
7514
6424
  }
7515
6425
  async function prepareCodexAttemptTools(runtime) {
7516
- const { connection, bundleMcpThreadConfig, bundleManifestRegistry, runtimeParams, effectiveRuntimeModelId, nativeToolSurfaceEnabled, nativeProviderWebSearchSupport, hookChannelId, codexMcpToolOverrides, authenticatedScheduledMode, ownsScheduledConfiguredMcpSurface, canResolveScheduledConfiguredMcpCreatorAuthority } = runtime;
7517
- const { params, preDynamicStartupStages, mutable, startupAuthProfileId, resolvedWorkspace, effectiveWorkspace, effectiveCwd, sandboxSessionKey, sandbox, sessionPermissionPolicy, runAbortController, sessionAgentId, pluginConfig, profilerEnabled, agentDir } = connection;
6426
+ const { connection, bundleMcpThreadConfig, bundleManifestRegistry, runtimeParams, effectiveRuntimeModelId, nativeToolSurfaceEnabled, nativeProviderWebSearchSupport, hookChannelId, codexMcpToolOverrides, authenticatedScheduledMode, configuredMcpSurface, canResolveScheduledConfiguredMcpCreatorAuthority } = runtime;
6427
+ const { params, preDynamicStartupStages, mutable, startupAuthProfileId, resolvedWorkspace, effectiveWorkspace, effectiveCwd, sandboxSessionKey, contextSessionKey, sandbox, sessionPermissionPolicy, runAbortController, sessionAgentId, policyAgentId, pluginConfig, profilerEnabled, agentDir } = connection;
7518
6428
  const preDynamicSummary = preDynamicStartupStages.snapshot();
7519
6429
  if (shouldWarnCodexDynamicToolBuildStageSummary(preDynamicSummary)) embeddedAgentLog.warn(`codex app-server pre-dynamic startup timings runId=${params.runId} sessionId=${params.sessionId} totalMs=${preDynamicSummary.totalMs} stages=${formatCodexDynamicToolBuildStageSummary(preDynamicSummary)}`, {
7520
6430
  runId: params.runId,
@@ -7557,20 +6467,29 @@ async function prepareCodexAttemptTools(runtime) {
7557
6467
  ...onCodexToolOutcome ? { onToolOutcome: onCodexToolOutcome } : {}
7558
6468
  };
7559
6469
  const computerContextEpoch = { value: 0 };
6470
+ const runCleanups = [];
7560
6471
  const cronCreatorToolAllowlist = [];
7561
6472
  const cronCreatorToolAllowlistCaptureRef = {};
7562
6473
  const scheduledAppAuthoritySourceRef = {};
7563
6474
  const preparedChatgptAuth = connection.startupPreparedAuth?.kind === "profile" && connection.startupPreparedAuth.snapshot?.loginParams.type === "chatgptAuthTokens" && connection.startupPreparedAuth.snapshot.chatgptAccountId ? {
6475
+ kind: "prepared-profile",
7564
6476
  profileId: connection.startupPreparedAuth.profileId,
7565
6477
  accountId: connection.startupPreparedAuth.snapshot.chatgptAccountId
7566
6478
  } : void 0;
6479
+ const configuredAppServerAuth = !preparedChatgptAuth && connection.appServer.start.transport !== "stdio" ? {
6480
+ kind: "configured-app-server",
6481
+ connectionFingerprint: buildScheduledCodexAppServerConnectionIdentity(connection.appServer)
6482
+ } : void 0;
6483
+ const scheduledCodexAppAuth = preparedChatgptAuth ?? configuredAppServerAuth;
7567
6484
  const appPolicy = resolveCodexPluginsPolicy(pluginConfig);
6485
+ const codexAppsMayBeVisible = appPolicy.enabled && (appPolicy.allowAllPlugins || appPolicy.pluginPolicies.some((entry) => entry.enabled));
7568
6486
  const appCreatorCapture = resolveScheduledCodexAppCreatorCaptureDecision({
7569
- appsMayBeVisible: appPolicy.enabled && (appPolicy.allowAllPlugins || appPolicy.pluginPolicies.some((entry) => entry.enabled)),
6487
+ appsMayBeVisible: codexAppsMayBeVisible,
7570
6488
  authenticatedScheduledMode,
7571
6489
  usesSupervisionConnection: connection.usesSupervisionConnection,
7572
6490
  homeScope: connection.appServer.start.homeScope,
7573
- hasPreparedAccountIdentity: Boolean(preparedChatgptAuth)
6491
+ hasPreparedAccountIdentity: Boolean(preparedChatgptAuth),
6492
+ hasConfiguredAppServerIdentity: Boolean(configuredAppServerAuth)
7574
6493
  });
7575
6494
  const codexAppAuthorityUnavailableReason = appCreatorCapture.unavailableReason;
7576
6495
  const canResolveScheduledCodexAppAuthority = appCreatorCapture.supported;
@@ -7581,6 +6500,7 @@ async function prepareCodexAttemptTools(runtime) {
7581
6500
  let resolveCreatorAuthorityImpl;
7582
6501
  const runtimeYieldCompletionClaim = {};
7583
6502
  const commonToolParams = {
6503
+ nodeExecAvailability: {},
7584
6504
  params: dynamicToolParams,
7585
6505
  resolvedWorkspace,
7586
6506
  effectiveWorkspace,
@@ -7592,6 +6512,7 @@ async function prepareCodexAttemptTools(runtime) {
7592
6512
  nativeProviderWebSearchSupport,
7593
6513
  runAbortController,
7594
6514
  sessionAgentId,
6515
+ policyAgentId,
7595
6516
  pluginConfig,
7596
6517
  profilerEnabled,
7597
6518
  ...params.cronCreatorAuthorityUnavailableReason === "queued-local-operator" && bundleMcpThreadConfig.staticServerNames.length > 0 ? { cronCreatorAuthorityUnavailableReason: "queued-local-operator-configured-mcp" } : {},
@@ -7616,8 +6537,35 @@ async function prepareCodexAttemptTools(runtime) {
7616
6537
  return pending;
7617
6538
  } } : {}
7618
6539
  };
6540
+ let nativeSpecs;
6541
+ if (hasCodexNativeToolCatalog(mutable.startupBinding)) {
6542
+ runAbortController.signal.throwIfAborted();
6543
+ params.hostCapabilities.assertActive();
6544
+ const client = await connection.attemptClientFactory({
6545
+ startOptions: connection.appServer.start,
6546
+ authProfileId: connection.startupClientAuthProfileId,
6547
+ agentDir,
6548
+ config: params.config,
6549
+ timeoutMs: connection.appServer.requestTimeoutMs
6550
+ });
6551
+ try {
6552
+ nativeSpecs = await loadCodexNativeToolCatalog({
6553
+ client,
6554
+ binding: mutable.startupBinding,
6555
+ appServer: connection.appServer,
6556
+ agentDir,
6557
+ assertCurrent: () => {
6558
+ runAbortController.signal.throwIfAborted();
6559
+ params.hostCapabilities.assertActive();
6560
+ }
6561
+ });
6562
+ } finally {
6563
+ releaseLeasedSharedCodexAppServerClient(client);
6564
+ }
6565
+ }
7619
6566
  const tools = await buildDynamicTools({
7620
6567
  ...commonToolParams,
6568
+ registerRunCleanup: (cleanup) => runCleanups.push(cleanup),
7621
6569
  cronCreatorToolAllowlistRef: cronCreatorToolAllowlist,
7622
6570
  cronCreatorToolAllowlistCaptureRef,
7623
6571
  onPersistentWebSearchPolicyResolved: (allowed) => {
@@ -7627,7 +6575,7 @@ async function prepareCodexAttemptTools(runtime) {
7627
6575
  toolState.webSearchAllowed = allowed;
7628
6576
  }
7629
6577
  });
7630
- const registeredTools = await buildDynamicTools({
6578
+ const registeredTools = nativeSpecs ? [] : await buildDynamicTools({
7631
6579
  ...commonToolParams,
7632
6580
  forceHeartbeatTool: true,
7633
6581
  ignoreDisableMessageTool: true,
@@ -7639,7 +6587,7 @@ async function prepareCodexAttemptTools(runtime) {
7639
6587
  runSessionKey: params.sessionKey && params.sessionKey !== sandboxSessionKey ? params.sessionKey : void 0,
7640
6588
  sessionId: params.sessionId,
7641
6589
  runId: params.runId,
7642
- agentId: sessionAgentId,
6590
+ agentId: policyAgentId,
7643
6591
  agentDir: agentDir ?? resolveAgentDir(params.config ?? {}, sessionAgentId),
7644
6592
  agentAccountId: params.agentAccountId,
7645
6593
  messageProvider: params.messageProvider ?? params.messageChannel,
@@ -7685,9 +6633,10 @@ async function prepareCodexAttemptTools(runtime) {
7685
6633
  ...params.memberRoleIds?.length ? { roleIds: [...params.memberRoleIds] } : {}
7686
6634
  };
7687
6635
  const hasRequester = Object.keys(requester).length > 0;
7688
- const scheduledConfiguredMcp = ownsScheduledConfiguredMcpSurface ? await materializeStaticMcpToolsForScheduledHarnessRun({
6636
+ const configuredMcp = configuredMcpSurface ? await materializeStaticMcpToolsForHarnessRun({
7689
6637
  sessionId: params.sessionId,
7690
6638
  sessionKey: params.sessionKey,
6639
+ agentId: sessionAgentId,
7691
6640
  workspaceDir: effectiveWorkspace,
7692
6641
  agentDir: policyContext.agentDir,
7693
6642
  cfg: params.config,
@@ -7696,6 +6645,29 @@ async function prepareCodexAttemptTools(runtime) {
7696
6645
  toolsAllow: params.toolsAllow,
7697
6646
  toolOverrides: codexMcpToolOverrides,
7698
6647
  autoApproveCodexAppServerApprovals: shouldAutoApproveCodexAppServerApprovals(connection.appServer),
6648
+ projectedMcpServers: bundleMcpThreadConfig.configPatch?.mcp_servers,
6649
+ ...configuredMcpSurface === "transient" ? { requestInteractiveCodexApproval: async (approval) => {
6650
+ const allowedDecisions = approval.mode === "prompt" ? ["allow-once", "deny"] : [
6651
+ "allow-once",
6652
+ "allow-always",
6653
+ "deny"
6654
+ ];
6655
+ const outcome = await requestPluginApprovalOutcome({
6656
+ hostCapabilities: params.hostCapabilities,
6657
+ signal: approval.signal,
6658
+ title: `Run MCP tool ${approval.serverName}/${approval.toolName}`,
6659
+ description: `Codex approval mode "${approval.mode}" requires an operator decision before this MCP tool runs. ${formatMcpCodexApprovalRemedy(approval.serverName)}`,
6660
+ allowedDecisions,
6661
+ toolName: approval.safeToolName,
6662
+ toolCallId: approval.toolCallId,
6663
+ mcpTool: {
6664
+ server: approval.serverName,
6665
+ tool: approval.toolName
6666
+ },
6667
+ isMcpToolApprovalActive: approval.isActive
6668
+ });
6669
+ if (outcome !== "approved-once" && outcome !== "approved-session") throw new Error(`${approval.serverName}/${approval.toolName}: interactive Codex approval (${approval.mode}) was not granted: ${outcome}`);
6670
+ } } : {},
7699
6671
  policyContext,
7700
6672
  warn: (message) => embeddedAgentLog.warn(message)
7701
6673
  }) : void 0;
@@ -7712,13 +6684,13 @@ async function prepareCodexAttemptTools(runtime) {
7712
6684
  requesterSenderId: params.senderId,
7713
6685
  agentAccountId: params.agentAccountId,
7714
6686
  messageChannel: params.messageChannel ?? params.messageProvider,
7715
- reservedToolNames,
6687
+ reservedToolNames: [...reservedToolNames, ...configuredMcp?.tools.map((tool) => tool.name) ?? []],
7716
6688
  toolsAllow: params.toolsAllow,
7717
6689
  policyContext,
7718
6690
  warn: (message) => embeddedAgentLog.warn(message)
7719
6691
  });
7720
- const scopedExecutable = filterCodexDynamicTools(scheduledConfiguredMcp?.tools ?? scopedMcpTools?.tools ?? [], pluginConfig);
7721
- const scopedAdvertised = filterCodexDynamicTools(scheduledConfiguredMcp?.tools ?? scopedMcpTools?.advertisedTools ?? [], pluginConfig);
6692
+ const scopedExecutable = filterCodexDynamicTools([...configuredMcp?.tools ?? [], ...scopedMcpTools?.tools ?? []], pluginConfig);
6693
+ const scopedAdvertised = filterCodexDynamicTools([...configuredMcp?.tools ?? [], ...scopedMcpTools?.advertisedTools ?? []], pluginConfig);
7722
6694
  const toolsWithScopedMcp = scopedExecutable.length > 0 ? [...tools, ...scopedExecutable] : tools;
7723
6695
  const registeredWithScopedMcp = scopedAdvertised.length > 0 ? [...registeredTools, ...scopedAdvertised] : registeredTools;
7724
6696
  const hookContext = {
@@ -7729,7 +6701,7 @@ async function prepareCodexAttemptTools(runtime) {
7729
6701
  remoteWorkspaceRoot: connection.appServer.remoteWorkspaceRoot,
7730
6702
  remoteWorkspaceRequestTimeoutMs: connection.appServer.requestTimeoutMs,
7731
6703
  sessionId: params.sessionId,
7732
- sessionKey: sandboxSessionKey,
6704
+ sessionKey: contextSessionKey,
7733
6705
  runId: params.runId,
7734
6706
  channelId: hookChannelId,
7735
6707
  currentChannelProvider: resolveCodexMessageToolProvider(params),
@@ -7753,6 +6725,7 @@ async function prepareCodexAttemptTools(runtime) {
7753
6725
  toolBridge = createCodexDynamicToolBridge({
7754
6726
  tools: toolsWithScopedMcp,
7755
6727
  registeredTools: registeredWithScopedMcp,
6728
+ registeredSpecs: nativeSpecs,
7756
6729
  signal: runAbortController.signal,
7757
6730
  computerContextEpoch,
7758
6731
  loading: resolveCodexDynamicToolsLoadingForRuntime(pluginConfig, effectiveRuntimeModelId, { connectionClass: connection.appServer.connectionClass }),
@@ -7771,9 +6744,9 @@ async function prepareCodexAttemptTools(runtime) {
7771
6744
  await captureFinalCodexCronCreatorToolAllowlist(authorityTools, captureRef, toolBridge.availableTools);
7772
6745
  if (!captureRef.value) throw new Error("cron creator authority snapshot did not produce provenance");
7773
6746
  const appSource = scheduledAppAuthoritySourceRef.current;
7774
- const runtimeAuthority = canResolveScheduledCodexAppAuthority && preparedChatgptAuth ? appSource ? await captureScheduledCodexAppAuthority({
6747
+ const runtimeAuthority = canResolveScheduledCodexAppAuthority && scheduledCodexAppAuth ? appSource ? await captureScheduledCodexAppAuthority({
7775
6748
  ...appSource,
7776
- ...preparedChatgptAuth,
6749
+ auth: scheduledCodexAppAuth,
7777
6750
  signal: options?.signal
7778
6751
  }) : (() => {
7779
6752
  throw new Error("Codex app authority is unavailable before the exact creator thread is active. Retry this automation mutation from the current owner turn.");
@@ -7789,8 +6762,9 @@ async function prepareCodexAttemptTools(runtime) {
7789
6762
  const authorityRuntimeId = `cron-authority:${params.runId}`;
7790
6763
  let materialized;
7791
6764
  try {
7792
- materialized = await materializeStaticMcpToolsForScheduledHarnessRun({
6765
+ materialized = await materializeStaticMcpToolsForHarnessRun({
7793
6766
  sessionId: authorityRuntimeId,
6767
+ agentId: sessionAgentId,
7794
6768
  workspaceDir: effectiveWorkspace,
7795
6769
  agentDir: policyContext.agentDir,
7796
6770
  cfg: params.config,
@@ -7799,6 +6773,7 @@ async function prepareCodexAttemptTools(runtime) {
7799
6773
  toolsAllow: params.toolsAllow,
7800
6774
  toolOverrides: codexMcpToolOverrides,
7801
6775
  autoApproveCodexAppServerApprovals: shouldAutoApproveCodexAppServerApprovals(connection.appServer),
6776
+ projectedMcpServers: bundleMcpThreadConfig.configPatch?.mcp_servers,
7802
6777
  policyContext,
7803
6778
  warn: (message) => embeddedAgentLog.warn(message),
7804
6779
  retireSessionRuntimeAfterDispose: true
@@ -7830,14 +6805,15 @@ async function prepareCodexAttemptTools(runtime) {
7830
6805
  tools: toolsWithScopedMcp,
7831
6806
  registeredTools: registeredWithScopedMcp,
7832
6807
  scopedMcpTools,
7833
- scheduledConfiguredMcp,
7834
- configuredMcpOwnershipVersion: ownsScheduledConfiguredMcpSurface ? 1 : void 0,
6808
+ configuredMcp,
6809
+ configuredMcpOwnershipVersion: configuredMcpSurface === "scheduled" ? 1 : void 0,
7835
6810
  cronCreatorToolAllowlist,
7836
6811
  cronCreatorToolAllowlistCaptureRef,
7837
6812
  scheduledAppAuthoritySourceRef,
7838
6813
  dynamicToolParams,
7839
6814
  compactionPlanState,
7840
6815
  computerContextEpoch,
6816
+ runCleanups,
7841
6817
  toolBridge,
7842
6818
  toolState,
7843
6819
  toolOutcomeOrdinals,
@@ -7848,7 +6824,7 @@ async function prepareCodexAttemptTools(runtime) {
7848
6824
  };
7849
6825
  } catch (error) {
7850
6826
  await scopedMcpTools?.dispose();
7851
- await scheduledConfiguredMcp?.dispose();
6827
+ await configuredMcp?.dispose();
7852
6828
  throw error;
7853
6829
  }
7854
6830
  }
@@ -7888,7 +6864,7 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
7888
6864
  const { context, turnState, buildRenderedCodexDeveloperInstructions } = prompt;
7889
6865
  const { runtime, attemptTools, hookContextWindowFields, workspaceBootstrapContext } = context;
7890
6866
  const { connection, runtimeParams, effectiveRuntimeProviderId, effectiveRuntimeModelId } = runtime;
7891
- const { tools } = attemptTools;
6867
+ const { tools, toolBridge } = attemptTools;
7892
6868
  const { params, usesSupervisionConnection, codexModelCallId, codexModelCallTrace, codexModelContentCapture, appServer, runAbortController } = connection;
7893
6869
  const { state } = turnRuntime;
7894
6870
  const explicitSkillInputs = await resolveCodexExplicitSkillInputs({
@@ -7951,7 +6927,8 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
7951
6927
  turnScopedDeveloperInstructions: workspaceBootstrapContext.turnScopedDeveloperInstructions,
7952
6928
  skillsCollaborationInstructions: context.skillsCollaborationInstructions,
7953
6929
  memoryCollaborationInstructions: workspaceBootstrapContext.memoryCollaborationInstructions,
7954
- preserveNativeTurnSettings: usesSupervisionConnection
6930
+ preserveNativeTurnSettings: usesSupervisionConnection,
6931
+ sessionStatusAvailable: toolBridge.availableTools.some((tool) => tool.name === "session_status")
7955
6932
  });
7956
6933
  codexModelCallDiagnostics.setRequestPayloadBytes(utf8JsonByteLength(turnStartParams));
7957
6934
  state.latestStartupErrorNotification = void 0;
@@ -8135,7 +7112,7 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
8135
7112
  trajectoryRecorder?.recordEvent("session.ended", {
8136
7113
  status: "error",
8137
7114
  threadId: resourceState.thread.threadId,
8138
- timedOut: state.timedOut,
7115
+ timedOut: state.timeout !== void 0,
8139
7116
  aborted: runAbortController.signal.aborted,
8140
7117
  promptError: message
8141
7118
  });
@@ -8156,8 +7133,7 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
8156
7133
  });
8157
7134
  const failureKind = classifyCodexModelCallFailureKind({
8158
7135
  error: turnStartError,
8159
- timedOut: state.timedOut,
8160
- turnCompletionIdleTimedOut: state.turnCompletionIdleTimedOut,
7136
+ timedOut: state.timeout !== void 0,
8161
7137
  runAborted: runAbortController.signal.aborted,
8162
7138
  abortReason: runAbortController.signal.reason,
8163
7139
  clientClosedAbort: state.clientClosedAbort,
@@ -8178,11 +7154,10 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
8178
7154
  ctx: hookContext,
8179
7155
  hookRunner
8180
7156
  });
8181
- const bindingReleased = isIncognitoSessionKey(params.sessionKey) ? await bindingStore.mutate(bindingIdentity, {
7157
+ if ((isIncognitoSessionKey(params.sessionKey) ? await bindingStore.mutate(bindingIdentity, {
8182
7158
  kind: "clear",
8183
7159
  threadId: resourceState.thread.threadId
8184
- }) : true;
8185
- if (!state.timedOut && bindingReleased && !resourceState.startupClientUnsafe) {
7160
+ }) : true) && !resourceState.startupClientUnsafe) {
8186
7161
  if (!await unsubscribeCodexThreadBestEffort(resourceState.client, {
8187
7162
  threadId: resourceState.thread.threadId,
8188
7163
  timeoutMs: 5e3
@@ -8255,268 +7230,56 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
8255
7230
  return { turn };
8256
7231
  }
8257
7232
  //#endregion
8258
- //#region extensions/codex/src/app-server/attempt-turn-watches.ts
8259
- /**
8260
- * Idle-watch controller for Codex app-server turn progress, completion, and
8261
- * terminal-event gaps.
8262
- */
8263
- /**
8264
- * Creates a controller that arms/disarms timers as Codex app-server
8265
- * notifications and tool handoffs progress.
8266
- */
8267
- function createCodexAttemptTurnWatchController(params) {
8268
- const timers = {};
8269
- let completionIdleWatchArmed = false;
8270
- let completionIdleWatchPinnedByTerminalError = false;
8271
- let completionIdleTimeoutOverrideMs;
8272
- let assistantCompletionIdleWatchArmed = false;
8273
- let assistantCompletionLastActivityAt = Date.now();
8274
- let assistantCompletionLastActivityDetails;
8275
- let attemptIdleWatchArmed = false;
8276
- let terminalIdleWatchArmed = false;
8277
- let completionLastActivityAt = Date.now();
8278
- let completionLastActivityReason = "startup";
8279
- let completionLastActivityDetails;
8280
- let attemptIdleTimeoutOverrideMs;
8281
- let attemptLastProgressAt = Date.now();
8282
- let attemptLastProgressReason = "startup";
8283
- let attemptLastProgressDetails;
8284
- const turnCompletionIdleTimeoutMs = resolveTimerTimeoutMs(params.turnCompletionIdleTimeoutMs, 1);
8285
- const turnAssistantCompletionIdleTimeoutMs = resolveTimerTimeoutMs(params.turnAssistantCompletionIdleTimeoutMs, 1);
8286
- const turnAttemptIdleTimeoutMs = resolveTimerTimeoutMs(params.turnAttemptIdleTimeoutMs, 1);
8287
- const turnTerminalIdleTimeoutMs = resolveTimerTimeoutMs(params.turnTerminalIdleTimeoutMs, 1);
8288
- const interruptTimeoutMs = resolveTimerTimeoutMs(params.interruptTimeoutMs, 1);
8289
- const resolveWatchTimeoutMs = (timeoutMs) => resolveTimerTimeoutMs(timeoutMs, 1);
8290
- const clearTimer = (kind) => {
8291
- const timer = timers[kind];
8292
- if (timer) {
8293
- clearTimeout(timer);
8294
- delete timers[kind];
8295
- }
8296
- };
8297
- const clearCompletionIdleTimer = () => clearTimer("completion");
8298
- const clearAllTimers = () => {
8299
- for (const kind of Object.keys(timers)) clearTimer(kind);
8300
- };
8301
- function scheduleWatch(kind, callback, lastActivityAt, timeoutMs, ready) {
8302
- clearTimer(kind);
8303
- if (!ready || params.isCompleted() || params.signal.aborted) return;
8304
- const elapsedMs = Math.max(0, Date.now() - lastActivityAt);
8305
- const timer = setTimeout(callback, Math.max(1, timeoutMs - elapsedMs));
8306
- timer.unref?.();
8307
- timers[kind] = timer;
8308
- }
8309
- function scheduleCompletionIdleWatch() {
8310
- scheduleWatch("completion", fireCompletionIdleTimeout, completionLastActivityAt, completionIdleTimeoutOverrideMs ?? turnCompletionIdleTimeoutMs, completionIdleWatchArmed && params.getActiveAppServerTurnRequests() === 0 && params.getActiveCompletionBlockerItemCount() === 0);
8311
- }
8312
- function scheduleAssistantCompletionIdleWatch() {
8313
- scheduleWatch("assistant", fireAssistantCompletionIdleRelease, assistantCompletionLastActivityAt, turnAssistantCompletionIdleTimeoutMs, assistantCompletionIdleWatchArmed && params.getActiveFinalizationHookCount() === 0);
8314
- }
8315
- function shouldPauseAttemptIdleWatch() {
8316
- if (params.getActiveAppServerTurnRequestsWithoutTimeout() > 0) return false;
8317
- return params.getActiveCompletionBlockerItemCount() > 0 || params.getActiveAppServerTurnRequests() > 0;
8318
- }
8319
- function scheduleAttemptIdleWatch() {
8320
- scheduleWatch("attempt", fireAttemptIdleTimeout, attemptLastProgressAt, attemptIdleTimeoutOverrideMs ?? turnAttemptIdleTimeoutMs, attemptIdleWatchArmed && !shouldPauseAttemptIdleWatch());
8321
- }
8322
- function scheduleTerminalIdleWatch() {
8323
- scheduleWatch("terminal", fireTerminalIdleTimeout, completionLastActivityAt, turnTerminalIdleTimeoutMs, terminalIdleWatchArmed && params.getActiveAppServerTurnRequests() === 0);
8324
- }
8325
- function scheduleProgressWatches() {
8326
- scheduleAttemptIdleWatch();
8327
- scheduleCompletionIdleWatch();
8328
- scheduleTerminalIdleWatch();
8329
- }
8330
- function isCompletionIdleTimeoutDueBeforeAttempt(timeoutMs) {
8331
- if (params.isCompleted() || params.isTerminalTurnNotificationQueued() || params.signal.aborted || !completionIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0 || params.getActiveCompletionBlockerItemCount() > 0) return false;
8332
- const completionTimeoutMs = completionIdleTimeoutOverrideMs ?? turnCompletionIdleTimeoutMs;
8333
- if (completionTimeoutMs > timeoutMs) return false;
8334
- return Math.max(0, Date.now() - completionLastActivityAt) >= completionTimeoutMs;
8335
- }
8336
- function recordAttemptProgress(reason, options) {
8337
- attemptIdleTimeoutOverrideMs = options?.attemptTimeoutMs !== void 0 ? resolveWatchTimeoutMs(options.attemptTimeoutMs) : void 0;
8338
- attemptLastProgressAt = completionLastActivityAt;
8339
- attemptLastProgressReason = reason;
8340
- attemptLastProgressDetails = options?.details;
8341
- params.onAttemptProgress(reason, options?.details);
8342
- scheduleAttemptIdleWatch();
8343
- }
8344
- function fireAssistantCompletionIdleRelease() {
8345
- if (params.isCompleted() || params.signal.aborted || !assistantCompletionIdleWatchArmed) return;
8346
- if (params.getActiveAppServerTurnRequests() > 0 || params.getActiveTurnItemCount() > 0 || params.getActiveFinalizationHookCount() > 0) {
8347
- scheduleAssistantCompletionIdleWatch();
8348
- return;
8349
- }
8350
- if (!params.canReleaseAssistantCompletionIdle()) {
8351
- assistantCompletionIdleWatchArmed = false;
8352
- assistantCompletionLastActivityDetails = void 0;
8353
- clearTimer("assistant");
8354
- return;
8355
- }
8356
- const idleMs = Math.max(0, Date.now() - assistantCompletionLastActivityAt);
8357
- if (idleMs < turnAssistantCompletionIdleTimeoutMs) {
8358
- scheduleAssistantCompletionIdleWatch();
8359
- return;
8360
- }
8361
- assistantCompletionIdleWatchArmed = false;
8362
- clearCompletionIdleTimer();
8363
- clearTimer("terminal");
8364
- const turnId = params.getTurnId();
8365
- const fields = {
8366
- threadId: params.threadId,
8367
- turnId,
8368
- idleMs,
8369
- timeoutMs: turnAssistantCompletionIdleTimeoutMs,
8370
- ...assistantCompletionLastActivityDetails
8371
- };
8372
- params.onRecordEvent("turn.assistant_completion_idle_release", fields);
8373
- embeddedAgentLog.warn("codex app-server turn released after completed assistant item without terminal event", fields);
8374
- if (turnId) {
8375
- params.onInterruptTurn({
8376
- threadId: params.threadId,
8377
- turnId,
8378
- timeoutMs: interruptTimeoutMs
8379
- }).finally(params.onCompleted);
8380
- return;
8381
- }
8382
- params.onCompleted();
8383
- }
8384
- function reportTimeout(timeout) {
8385
- params.onTimeout(timeout);
8386
- const fields = {
8387
- threadId: params.threadId,
8388
- turnId: params.getTurnId(),
8389
- idleMs: timeout.idleMs,
8390
- timeoutMs: timeout.timeoutMs,
8391
- lastActivityReason: timeout.lastActivityReason,
8392
- ...timeout.details
8393
- };
8394
- params.onRecordEvent(`turn.${timeout.kind}_idle_timeout`, fields);
8395
- embeddedAgentLog.warn(`codex app-server turn idle timed out waiting for ${timeout.kind === "terminal" ? "terminal event" : timeout.kind}`, fields);
8396
- params.onAbort(`turn_${timeout.kind}_idle_timeout`);
8397
- }
8398
- function fireAttemptIdleTimeout() {
8399
- if (params.isCompleted() || params.signal.aborted || !attemptIdleWatchArmed || shouldPauseAttemptIdleWatch()) return;
8400
- const idleMs = Math.max(0, Date.now() - attemptLastProgressAt);
8401
- const timeoutMs = attemptIdleTimeoutOverrideMs ?? turnAttemptIdleTimeoutMs;
8402
- if (idleMs < timeoutMs) {
8403
- scheduleAttemptIdleWatch();
8404
- return;
8405
- }
8406
- if (isCompletionIdleTimeoutDueBeforeAttempt(timeoutMs)) {
8407
- fireCompletionIdleTimeout();
8408
- return;
8409
- }
8410
- reportTimeout({
8411
- kind: "progress",
8412
- idleMs,
8413
- timeoutMs,
8414
- lastActivityReason: attemptLastProgressReason,
8415
- details: attemptLastProgressDetails
8416
- });
8417
- }
8418
- function fireCompletionIdleTimeout() {
8419
- if (params.isCompleted() || params.isTerminalTurnNotificationQueued() || params.signal.aborted || !completionIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0 || params.getActiveCompletionBlockerItemCount() > 0) return;
8420
- const timeoutMs = completionIdleTimeoutOverrideMs ?? turnCompletionIdleTimeoutMs;
8421
- const idleMs = Math.max(0, Date.now() - completionLastActivityAt);
8422
- if (idleMs < timeoutMs) {
8423
- scheduleCompletionIdleWatch();
8424
- return;
8425
- }
8426
- const details = {
8427
- ...completionLastActivityDetails,
8428
- activeAppServerTurnRequests: params.getActiveAppServerTurnRequests(),
8429
- activeTurnItemCount: params.getActiveTurnItemCount(),
8430
- terminalTurnNotificationQueued: params.isTerminalTurnNotificationQueued(),
8431
- completionIdleWatchArmed,
8432
- assistantCompletionIdleWatchArmed,
8433
- terminalIdleWatchArmed
8434
- };
8435
- reportTimeout({
8436
- kind: "completion",
8437
- idleMs,
8438
- timeoutMs,
8439
- lastActivityReason: completionLastActivityReason,
8440
- details
7233
+ //#region extensions/codex/src/app-server/attempt-deadlines.ts
7234
+ function createCodexAttemptDeadlineController(params) {
7235
+ let deadline = { kind: "closed" };
7236
+ let timer;
7237
+ const armDeadline = (next) => {
7238
+ clearTimeout(timer);
7239
+ deadline = next;
7240
+ const deadlineAtMs = next.startedAtMs + next.timeoutMs;
7241
+ params.onDeadlineChanged?.({
7242
+ kind: "bounded",
7243
+ deadlineAtMs
8441
7244
  });
8442
- }
8443
- function fireTerminalIdleTimeout() {
8444
- if (params.isCompleted() || params.isTerminalTurnNotificationQueued() || params.signal.aborted || !terminalIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0) return;
8445
- const idleMs = Math.max(0, Date.now() - completionLastActivityAt);
8446
- if (idleMs < turnTerminalIdleTimeoutMs) {
8447
- scheduleTerminalIdleWatch();
8448
- return;
8449
- }
8450
- reportTimeout({
8451
- kind: "terminal",
8452
- idleMs,
8453
- timeoutMs: turnTerminalIdleTimeoutMs,
8454
- lastActivityReason: completionLastActivityReason,
8455
- details: completionLastActivityDetails
7245
+ timer = setTimeout(() => {
7246
+ if (deadline !== next || params.signal.aborted) return;
7247
+ deadline = { kind: "closed" };
7248
+ params.onTimeout({
7249
+ kind: next.kind,
7250
+ elapsedMs: Math.max(0, Date.now() - next.startedAtMs),
7251
+ timeoutMs: next.timeoutMs
7252
+ });
7253
+ }, resolveTimerTimeoutMs(Math.max(1, deadlineAtMs - Date.now()), 1));
7254
+ timer.unref?.();
7255
+ };
7256
+ const dispose = () => {
7257
+ deadline = { kind: "closed" };
7258
+ clearTimeout(timer);
7259
+ params.signal.removeEventListener("abort", dispose);
7260
+ };
7261
+ if (!params.signal.aborted) {
7262
+ params.signal.addEventListener("abort", dispose, { once: true });
7263
+ if (params.timeoutMs >= MAX_TIMER_TIMEOUT_MS) {
7264
+ deadline = { kind: "unlimited" };
7265
+ params.onDeadlineChanged?.({ kind: "unlimited" });
7266
+ } else armDeadline({
7267
+ kind: "execution",
7268
+ startedAtMs: params.startedAtMs,
7269
+ timeoutMs: params.timeoutMs
8456
7270
  });
8457
7271
  }
8458
7272
  return {
8459
- isCompletionIdleWatchArmed: () => completionIdleWatchArmed,
8460
- isCompletionIdleWatchPinnedByTerminalError: () => completionIdleWatchPinnedByTerminalError,
8461
- isAssistantCompletionIdleWatchArmed: () => assistantCompletionIdleWatchArmed,
8462
- armAttemptIdleWatch: () => {
8463
- attemptIdleWatchArmed = true;
8464
- scheduleAttemptIdleWatch();
8465
- },
8466
- armTerminalIdleWatch: () => {
8467
- terminalIdleWatchArmed = true;
8468
- scheduleTerminalIdleWatch();
8469
- },
8470
- armCompletionIdleWatch: (options) => {
8471
- completionIdleWatchArmed = true;
8472
- completionIdleWatchPinnedByTerminalError = options?.pinnedByTerminalError === true;
8473
- completionIdleTimeoutOverrideMs = options?.timeoutMs !== void 0 ? resolveWatchTimeoutMs(options.timeoutMs) : void 0;
8474
- scheduleCompletionIdleWatch();
8475
- },
8476
- disarmCompletionIdleWatch: () => {
8477
- completionIdleWatchArmed = false;
8478
- completionIdleWatchPinnedByTerminalError = false;
8479
- completionIdleTimeoutOverrideMs = void 0;
8480
- clearCompletionIdleTimer();
8481
- },
8482
- armAssistantCompletionIdleWatch: (details) => {
8483
- assistantCompletionIdleWatchArmed = true;
8484
- assistantCompletionLastActivityAt = Date.now();
8485
- assistantCompletionLastActivityDetails = details;
8486
- scheduleAssistantCompletionIdleWatch();
8487
- },
8488
- disarmAssistantCompletionIdleWatch: () => {
8489
- assistantCompletionIdleWatchArmed = false;
8490
- assistantCompletionLastActivityDetails = void 0;
8491
- clearTimer("assistant");
8492
- },
8493
- touchActivity: (reason, options) => {
8494
- completionLastActivityAt = Date.now();
8495
- completionLastActivityReason = reason;
8496
- completionLastActivityDetails = options?.details;
8497
- completionIdleTimeoutOverrideMs = void 0;
8498
- if (options?.attemptProgress) recordAttemptProgress(reason, options);
8499
- params.onProgressDiagnostic(reason);
8500
- if (options?.arm) {
8501
- completionIdleWatchArmed = true;
8502
- completionIdleWatchPinnedByTerminalError = false;
8503
- }
8504
- scheduleProgressWatches();
8505
- },
8506
- noteNotificationReceived: (method, options) => {
8507
- const now = Date.now();
8508
- completionLastActivityAt = Math.max(completionLastActivityAt, Math.min(now, options?.receivedAtMs ?? now));
8509
- completionLastActivityReason = `notification:${method}`;
8510
- if (options?.details !== void 0) completionLastActivityDetails = options.details;
8511
- if (options?.attemptProgress) recordAttemptProgress(completionLastActivityReason, options);
8512
- },
8513
- extendAttemptIdleWatch: (timeoutMs) => {
8514
- attemptIdleTimeoutOverrideMs = resolveWatchTimeoutMs(timeoutMs);
8515
- scheduleAttemptIdleWatch();
7273
+ ownsExecutionWait: () => deadline.kind === "unlimited" || deadline.kind === "execution" && Date.now() < deadline.startedAtMs + deadline.timeoutMs,
7274
+ beginSettlement: (startedAtMs) => {
7275
+ if (deadline.kind === "closed" || deadline.kind === "settlement") return;
7276
+ armDeadline({
7277
+ kind: "settlement",
7278
+ startedAtMs,
7279
+ timeoutMs: TURN_TERMINAL_SETTLEMENT_TIMEOUT_MS
7280
+ });
8516
7281
  },
8517
- scheduleProgressWatches,
8518
- clearCompletionIdleTimer,
8519
- clearAllTimers
7282
+ dispose
8520
7283
  };
8521
7284
  }
8522
7285
  //#endregion
@@ -8524,24 +7287,18 @@ function createCodexAttemptTurnWatchController(params) {
8524
7287
  const CODEX_NATIVE_HOOK_RELAY_RENEW_INTERVAL_MS = 6e4;
8525
7288
  function createCodexAttemptTurnState(resources) {
8526
7289
  const { prompt, state: resourceState, projectorRef, trajectoryRecorder, startupTimeoutMs } = resources;
8527
- const { context } = prompt;
8528
- const { connection } = context.runtime;
8529
- const { params, options, appServer, runAbortController } = connection;
7290
+ const { connection } = prompt.context.runtime;
7291
+ const { params, options, runAbortController } = connection;
8530
7292
  const state = {
8531
7293
  latestStartupErrorNotification: void 0,
8532
7294
  rateLimitsRevisionBeforeLastTurnStart: void 0,
8533
7295
  completed: false,
7296
+ abortCleanup: Promise.resolve(),
7297
+ permissionChangeRestart: void 0,
8534
7298
  localCompletionRequested: false,
8535
7299
  terminalTurnNotificationQueued: false,
8536
7300
  sawCodexInterruptMarker: false,
8537
- timedOut: false,
8538
- turnCompletionIdleTimedOut: false,
8539
- turnWatchTimeoutKind: void 0,
8540
- turnWatchTimeoutIdleMs: void 0,
8541
- turnWatchTimeoutMs: void 0,
8542
- turnWatchTimeoutLastActivityReason: void 0,
8543
- turnWatchTimeoutDetails: void 0,
8544
- turnCompletionIdleTimeoutMessage: void 0,
7301
+ timeout: void 0,
8545
7302
  clientClosedPromptError: void 0,
8546
7303
  clientClosedDiagnostic: void 0,
8547
7304
  clientClosedAbort: false,
@@ -8550,26 +7307,16 @@ function createCodexAttemptTurnState(resources) {
8550
7307
  lifecycleTerminalEmitted: false,
8551
7308
  nativeHookRelayLastRenewedAt: 0,
8552
7309
  activeAppServerTurnRequests: 0,
8553
- activeAppServerTurnRequestsWithoutTimeout: 0,
8554
- unsettledFinalizationHookCount: 0,
8555
- rejectedFinalizationHookAssistant: void 0,
8556
- turnCrossedToolHandoff: false,
7310
+ activeLocalProjections: 0,
7311
+ projectionClosed: false,
8557
7312
  pendingTerminalDynamicToolRelease: void 0,
8558
7313
  terminalDynamicToolReleaseCheckScheduled: false,
8559
7314
  currentTurnHadNonTerminalDynamicToolResult: false
8560
7315
  };
8561
7316
  const { promise: completion, resolve: resolveCompletion } = createDeferred();
8562
- const turnCompletionIdleTimeoutMs = resolveCodexTurnCompletionIdleTimeoutMs(options.turnCompletionIdleTimeoutMs ?? appServer.turnCompletionIdleTimeoutMs);
8563
- const turnAssistantCompletionIdleTimeoutMs = resolveCodexTurnAssistantCompletionIdleTimeoutMs(options.turnAssistantCompletionIdleTimeoutMs ?? appServer.turnAssistantCompletionIdleTimeoutMs);
8564
- const postToolRawAssistantCompletionIdleTimeoutMs = resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs(options.postToolRawAssistantCompletionIdleTimeoutMs ?? appServer.postToolRawAssistantCompletionIdleTimeoutMs, turnAssistantCompletionIdleTimeoutMs);
8565
- const turnTerminalIdleTimeoutMs = resolveCodexTurnTerminalIdleTimeoutMs(options.turnTerminalIdleTimeoutMs, params.runTimeoutOverrideMs);
8566
- const turnAttemptIdleTimeoutMs = Math.max(100, Math.floor(params.timeoutMs));
8567
7317
  const pendingOpenClawDynamicToolCompletionIds = /* @__PURE__ */ new Set();
8568
7318
  const openClawDynamicToolExecutions = createCodexDynamicToolExecutionRegistry();
8569
7319
  const activeTurnItemIds = /* @__PURE__ */ new Set();
8570
- const activeCompletionBlockerItemIds = /* @__PURE__ */ new Set();
8571
- const activeFinalizationHookRunIds = /* @__PURE__ */ new Set();
8572
- const finalizationHookBatchStatuses = /* @__PURE__ */ new Map();
8573
7320
  const turnIdRef = {};
8574
7321
  const userInputBridgeRef = {};
8575
7322
  const steeringQueueRef = {};
@@ -8577,7 +7324,7 @@ function createCodexAttemptTurnState(resources) {
8577
7324
  if (state.completed) return;
8578
7325
  state.completed = true;
8579
7326
  steeringQueueRef.current?.cancel();
8580
- turnWatches.clearAllTimers();
7327
+ deadlines.beginSettlement(Date.now());
8581
7328
  resolveCompletion();
8582
7329
  };
8583
7330
  const interruptTurn = async (turnId, completionOptions) => {
@@ -8599,86 +7346,62 @@ function createCodexAttemptTurnState(resources) {
8599
7346
  state.nativeHookRelayLastRenewedAt = now;
8600
7347
  resourceState.nativeHookRelay.renew(resolveCodexNativeHookRelayTtlMs({
8601
7348
  explicitTtlMs: void 0,
8602
- attemptTimeoutMs: turnAttemptIdleTimeoutMs,
7349
+ attemptTimeoutMs: params.timeoutMs,
8603
7350
  startupTimeoutMs,
8604
7351
  turnStartTimeoutMs: params.timeoutMs
8605
7352
  }));
8606
7353
  };
8607
- const turnWatches = createCodexAttemptTurnWatchController({
8608
- threadId: resourceState.thread.threadId,
7354
+ const noteProgress = (reason) => {
7355
+ if (state.completed || state.projectionClosed || runAbortController.signal.aborted) return;
7356
+ renewNativeHookRelayForTurnProgress();
7357
+ params.onRunProgress?.({
7358
+ reason,
7359
+ provider: params.provider,
7360
+ model: params.modelId,
7361
+ backend: "codex-app-server"
7362
+ });
7363
+ emitTrustedDiagnosticEvent({
7364
+ type: "run.progress",
7365
+ runId: params.runId,
7366
+ sessionId: params.sessionId,
7367
+ sessionKey: params.sessionKey,
7368
+ reason: `codex_app_server:${reason}`
7369
+ });
7370
+ };
7371
+ const deadlines = createCodexAttemptDeadlineController({
7372
+ startedAtMs: connection.attemptStartedAt,
7373
+ timeoutMs: params.timeoutMs,
8609
7374
  signal: runAbortController.signal,
8610
- getTurnId: () => turnIdRef.current,
8611
- isCompleted: () => state.completed,
8612
- isTerminalTurnNotificationQueued: () => state.terminalTurnNotificationQueued,
8613
- getActiveAppServerTurnRequests: () => state.activeAppServerTurnRequests,
8614
- getActiveAppServerTurnRequestsWithoutTimeout: () => state.activeAppServerTurnRequestsWithoutTimeout,
8615
- getActiveTurnItemCount: () => activeTurnItemIds.size,
8616
- getActiveCompletionBlockerItemCount: () => activeCompletionBlockerItemIds.size,
8617
- getActiveFinalizationHookCount: () => state.unsettledFinalizationHookCount,
8618
- canReleaseAssistantCompletionIdle: () => projectorRef.current?.hasLatestTerminalAssistantCandidateText() === true,
8619
- turnCompletionIdleTimeoutMs,
8620
- turnAssistantCompletionIdleTimeoutMs,
8621
- turnAttemptIdleTimeoutMs,
8622
- turnTerminalIdleTimeoutMs,
8623
- interruptTimeoutMs: CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS,
8624
- onInterruptTurn: ({ turnId, timeoutMs }) => interruptTurn(turnId, {
8625
- locallyCompleted: true,
8626
- timeoutMs
8627
- }),
7375
+ onDeadlineChanged: params.onAttemptDeadlineChanged,
8628
7376
  onTimeout: (timeout) => {
8629
- state.timedOut = true;
8630
- state.turnCompletionIdleTimedOut = true;
8631
- state.turnWatchTimeoutKind = timeout.kind;
8632
- state.turnWatchTimeoutIdleMs = timeout.idleMs;
8633
- state.turnWatchTimeoutMs = timeout.timeoutMs;
8634
- state.turnWatchTimeoutLastActivityReason = timeout.lastActivityReason;
8635
- state.turnWatchTimeoutDetails = timeout.details;
8636
- state.turnCompletionIdleTimeoutMessage = "codex app-server turn idle timed out waiting for turn/completed";
7377
+ state.timeout = timeout;
8637
7378
  projectorRef.current?.markTimedOut();
8638
- },
8639
- onAbort: (reason) => runAbortController.abort(reason),
8640
- onCompleted: completeTurn,
8641
- onRecordEvent: (name, fields) => trajectoryRecorder?.recordEvent(name, fields),
8642
- onAttemptProgress: (reason) => {
8643
- renewNativeHookRelayForTurnProgress();
8644
- params.onRunProgress?.({
8645
- reason,
8646
- provider: params.provider,
8647
- model: params.modelId,
8648
- backend: "codex-app-server"
8649
- });
8650
- },
8651
- onProgressDiagnostic: (reason) => {
8652
- emitTrustedDiagnosticEvent({
8653
- type: "run.progress",
8654
- runId: params.runId,
8655
- sessionId: params.sessionId,
8656
- sessionKey: params.sessionKey,
8657
- reason: `codex_app_server:${reason}`
8658
- });
7379
+ const error = /* @__PURE__ */ new Error(timeout.kind === "execution" ? "codex app-server execution budget timed out" : "codex app-server terminal settlement timed out");
7380
+ const fields = {
7381
+ threadId: resourceState.thread.threadId,
7382
+ turnId: turnIdRef.current,
7383
+ ...timeout
7384
+ };
7385
+ trajectoryRecorder?.recordEvent(`turn.${timeout.kind}_timeout`, fields);
7386
+ embeddedAgentLog.warn(error.message, fields);
7387
+ params.onAttemptTimeout?.(error);
7388
+ runAbortController.abort(error);
8659
7389
  }
8660
7390
  });
8661
7391
  return {
8662
7392
  state,
8663
7393
  completion,
8664
- turnCompletionIdleTimeoutMs,
8665
- turnAssistantCompletionIdleTimeoutMs,
8666
- postToolRawAssistantCompletionIdleTimeoutMs,
8667
- turnTerminalIdleTimeoutMs,
8668
- turnAttemptIdleTimeoutMs,
8669
7394
  pendingOpenClawDynamicToolCompletionIds,
8670
7395
  openClawDynamicToolExecutions,
8671
7396
  activeTurnItemIds,
8672
- activeCompletionBlockerItemIds,
8673
- activeFinalizationHookRunIds,
8674
- finalizationHookBatchStatuses,
8675
7397
  turnIdRef,
8676
7398
  userInputBridgeRef,
8677
7399
  steeringQueueRef,
8678
7400
  completeTurn,
8679
7401
  interruptTurn,
8680
7402
  renewNativeHookRelayForTurnProgress,
8681
- turnWatches
7403
+ noteProgress,
7404
+ deadlines
8682
7405
  };
8683
7406
  }
8684
7407
  //#endregion
@@ -8694,21 +7417,26 @@ async function runCodexAppServerAttempt(params, options) {
8694
7417
  attemptTools.runtimeYieldCompletionClaim.current = () => resources.state.nativeHookRelay?.hasClaimedDirectChild() ?? false;
8695
7418
  await startCodexAttemptRuntime(resources);
8696
7419
  const turnRuntime = createCodexAttemptTurnState(resources);
8697
- const lifecycle = createCodexAttemptLifecycleController(resources, turnRuntime);
8698
- const notifications = createCodexAttemptNotificationController(resources, turnRuntime, lifecycle);
8699
- const { ensureCurrentThreadRoute } = await prepareCodexAttemptRoute(resources, turnRuntime, notifications, createCodexAttemptServerRequestController(resources, turnRuntime, lifecycle).handleServerRequest);
8700
- const turnRequest = await prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurrentThreadRoute, notifications.waitForActiveNativeTurnCompletion);
8701
- const turnStart = await startCodexAttemptTurn(resources, turnRuntime, notifications, turnRequest);
8702
- if ("result" in turnStart) return turnStart.result;
8703
- const activeTurn = await activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notifications, turnStart.turn);
8704
- let finalizedResult;
8705
7420
  try {
8706
- finalizedResult = await finalizeCodexAttempt(resources, turnRuntime, lifecycle, notifications, turnRequest, activeTurn);
7421
+ const lifecycle = createCodexAttemptLifecycleController(resources, turnRuntime);
7422
+ const notifications = createCodexAttemptNotificationController(resources, turnRuntime, lifecycle);
7423
+ const { ensureCurrentThreadRoute } = await prepareCodexAttemptRoute(resources, turnRuntime, notifications, createCodexAttemptServerRequestController(resources, turnRuntime, lifecycle).handleServerRequest);
7424
+ const turnRequest = await prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurrentThreadRoute, notifications.waitForActiveNativeTurnCompletion);
7425
+ const turnStart = await startCodexAttemptTurn(resources, turnRuntime, notifications, turnRequest);
7426
+ if ("result" in turnStart) return turnStart.result;
7427
+ const activeTurn = activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notifications, turnStart.turn);
7428
+ let finalizedResult;
7429
+ try {
7430
+ await activeTurn.ready;
7431
+ finalizedResult = await finalizeCodexAttempt(resources, turnRuntime, lifecycle, notifications, turnRequest, activeTurn);
7432
+ } finally {
7433
+ await cleanupCodexAttempt(resources, turnRuntime, lifecycle, turnRequest, activeTurn);
7434
+ }
7435
+ if (resources.state.executionDisconnectError && !connection.terminalState.explicitCancellationObserved) throw resources.state.executionDisconnectError;
7436
+ return finalizedResult;
8707
7437
  } finally {
8708
- await cleanupCodexAttempt(resources, turnRuntime, lifecycle, turnRequest, activeTurn);
7438
+ turnRuntime.deadlines.dispose();
8709
7439
  }
8710
- if (resources.state.executionDisconnectError && !connection.terminalState.explicitCancellationObserved) throw resources.state.executionDisconnectError;
8711
- return finalizedResult;
8712
7440
  }
8713
7441
  //#endregion
8714
7442
  export { runCodexAppServerAttempt };